source('../env.R')
Using GitHub PAT from the git credential store.
Skipping install of 'clootl' from a github remote, the SHA1 (8ab71cca) has not changed since last install.
  Use `force = TRUE` to force installation
community_data = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'community_assembly_metrics_using_relative_abundance.csv'))
Rows: 308 Columns: 10── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (10): mntd_standard, mntd_actual, mass_fdiv_standard, mass_fdiv_actual, beak_width_fdiv_standard, beak_width_fdiv_actual, hwi_fdiv_standard, hwi_fdiv_actual, city_id, u...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(community_data)
colnames(community_data)
 [1] "mntd_standard"            "mntd_actual"              "mass_fdiv_standard"       "mass_fdiv_actual"         "beak_width_fdiv_standard" "beak_width_fdiv_actual"  
 [7] "hwi_fdiv_standard"        "hwi_fdiv_actual"          "city_id"                  "urban_pool_size"         
min(community_data$mntd_standard)
[1] -2.33692
max(community_data$mntd_standard)
[1] 2.328448
min(community_data$beak_width_fdiv_standard)
[1] -2.685152
max(community_data$beak_width_fdiv_standard)
[1] 1.931681
min(community_data$hwi_fdiv_standard)
[1] -2.200336
max(community_data$hwi_fdiv_standard)
[1] 2.333383
min(community_data$mass_fdiv_standard)
[1] -2.377212
max(community_data$mass_fdiv_standard)
[1] 2.1073

Join on realms

city_to_realm = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv'))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
community_data_with_realm = left_join(community_data, city_to_realm)
Joining with `by = join_by(city_id)`

Cities as points

city_points = st_centroid(read_sf(filename(CITY_DATA_OUTPUT_DIR, 'city_selection.shp'))) %>% left_join(community_data_with_realm)
Warning: st_centroid assumes attributes are constant over geometriesWarning: st_centroid does not give correct centroids for longitude/latitude dataJoining with `by = join_by(city_id)`
city_points_coords = st_coordinates(city_points)
city_points$latitude = city_points_coords[,1]
city_points$longitude = city_points_coords[,2]
world_map = read_country_boundaries()

Load community data, and create long format version

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
communities
community_summary = communities %>% group_by(city_id) %>% summarise(regional_pool_size = n(), urban_pool_size = sum(relative_abundance_proxy > 0))
community_summary

Load trait data

traits = read_csv(filename(TAXONOMY_OUTPUT_DIR, 'traits_ebird.csv'))
Rows: 332 Columns: 10── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): ebird_species_name, habitat, trophic_level, trophic_niche, primary_lifestyle
dbl (5): beak_width, hwi, mass, habitat_density, migration
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(traits)

Load spatial var

spatial_var = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'spatial_var.csv')) %>% filter(city_id %in% community_summary$city_id)
Rows: 337 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (3): city_id, NMDS1, NMDS2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
spatial_var

Summary metrics by Realm

test_required_values = function(name, df) {
  cat(paste(
    test_value_wilcox(paste(name, 'MNTD'), df$mntd_standard),
    test_value_wilcox(paste(name, 'Beak Width FDiv'), df$beak_width_fdiv_standard),
    test_value_wilcox(paste(name, 'HWI FDiv'), df$hwi_fdiv_standard),
    test_value_wilcox(paste(name, 'Mass FDiv'), df$mass_fdiv_standard),
    paste('N', nrow(df)),
    sep = "\n"))
}
test_required_values('Global', community_data_with_realm)
Global MNTD median -0.36 ***
Global Beak Width FDiv median 0.02 
Global HWI FDiv median 0.39 **
Global Mass FDiv median 0.29 ***
N 308
unique(community_data_with_realm$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic"  "Indomalayan" "Australasia"
test_required_values('Nearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Nearctic',])
Nearctic MNTD median 0.67 *
Nearctic Beak Width FDiv median 0.29 
Nearctic HWI FDiv median -0.8 ***
Nearctic Mass FDiv median -0.26 
N 46
test_required_values('Neotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Neotropic',])
Neotropic MNTD median 0.03 
Neotropic Beak Width FDiv median -0.44 ***
Neotropic HWI FDiv median -0.31 
Neotropic Mass FDiv median 0.33 *
N 64
test_required_values('Palearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Palearctic',])
Palearctic MNTD median 0.13 
Palearctic Beak Width FDiv median 1.25 ***
Palearctic HWI FDiv median -0.39 
Palearctic Mass FDiv median 0.01 
N 72
test_required_values('Afrotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Afrotropic',])
Afrotropic MNTD median -1.28 *
Afrotropic Beak Width FDiv median -0.56 
Afrotropic HWI FDiv median 0.15 
Afrotropic Mass FDiv median -0.95 
N 9
test_required_values('Indomalayan', community_data_with_realm[community_data_with_realm$core_realm == 'Indomalayan',])
Indomalayan MNTD median -0.64 ***
Indomalayan Beak Width FDiv median -0.68 ***
Indomalayan HWI FDiv median 1.11 ***
Indomalayan Mass FDiv median 0.83 ***
N 111
test_required_values('Australasia', community_data_with_realm[community_data_with_realm$core_realm == 'Australasia',])
Australasia MNTD median -1.39 
Australasia Beak Width FDiv median -0.75 
Australasia HWI FDiv median 0.77 
Australasia Mass FDiv median -0.96 
N 6

How significant are those differences:

kruskal.test(mntd_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mntd_standard by core_realm
Kruskal-Wallis chi-squared = 102.25, df = 5, p-value < 0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$mntd_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mntd_standard and community_data_with_realm$core_realm 

            Afrotropic      Australasia Indomalayan     Nearctic Neotropic
Australasia 1.00000         -           -               -        -        
Indomalayan 0.00423         0.01024     -               -        -        
Nearctic    0.0000031706079 0.00018     0.0000000148738 -        -        
Neotropic   0.00018         0.00170     0.0000000095796 0.19502  -        
Palearctic  0.00018         0.00170     0.0000000000072 0.04492  1.00000  

P value adjustment method: holm 
kruskal.test(beak_width_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  beak_width_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 108.07, df = 5, p-value < 0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$beak_width_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$beak_width_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan          Nearctic          Neotropic        
Australasia 1.00000    -           -                    -                 -                
Indomalayan 1.00000    1.00000     -                    -                 -                
Nearctic    0.11361    1.00000     0.00131              -                 -                
Neotropic   1.00000    1.00000     1.00000              0.00371           -                
Palearctic  0.00026    0.13449     < 0.0000000000000002 0.000001417670330 0.000000000000017

P value adjustment method: holm 
kruskal.test(hwi_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  hwi_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 115.12, df = 5, p-value < 0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$hwi_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$hwi_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan         Nearctic Neotropic
Australasia 1.0000     -           -                   -        -        
Indomalayan 0.1553     1.0000      -                   -        -        
Nearctic    1.0000     0.0045      0.00000000000000046 -        -        
Neotropic   1.0000     0.0680      0.00000000000000269 0.1028   -        
Palearctic  1.0000     0.3068      0.00000000003391888 0.2673   1.0000   

P value adjustment method: holm 
kruskal.test(mass_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mass_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 49.157, df = 5, p-value = 0.000000002061
pairwise.wilcox.test(community_data_with_realm$mass_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mass_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan Nearctic Neotropic
Australasia 1.0000     -           -           -        -        
Indomalayan 0.0031     0.1307      -           -        -        
Nearctic    0.1307     1.0000      0.0021      -        -        
Neotropic   0.0132     0.2989      1.0000      0.1307   -        
Palearctic  0.0731     1.0000      0.000000021 1.0000   0.0599   

P value adjustment method: holm 

Summary metrics by invasive species

cities_with_introduced_species = communities %>% filter(origin == 'Introduced') %>% select(city_id) %>% distinct()

cities_with_no_introduced_species = communities %>% filter(!(city_id %in% cities_with_introduced_species$city_id)) %>% select(city_id) %>% distinct()

cities_with_introduced_species$introduced_species = TRUE
cities_with_no_introduced_species$introduced_species = FALSE

community_data_with_realm_with_introduced = community_data_with_realm %>% left_join(rbind(cities_with_introduced_species, cities_with_no_introduced_species))
Joining with `by = join_by(city_id)`
community_data_with_realm_with_introduced
test_required_values('With Introduced', community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$introduced_species,])
With Introduced MNTD median -0.03 
With Introduced Beak Width FDiv median 0.11 
With Introduced HWI FDiv median -0.36 
With Introduced Mass FDiv median 0.01 
N 189
test_required_values('Without Introduced', community_data_with_realm_with_introduced[!community_data_with_realm_with_introduced$introduced_species,])
Without Introduced MNTD median -0.53 ***
Without Introduced Beak Width FDiv median -0.28 *
Without Introduced HWI FDiv median 1.04 ***
Without Introduced Mass FDiv median 0.72 ***
N 119

How significant are those differences:

wilcox.test(community_data_with_realm_with_introduced$mntd_standard~community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  community_data_with_realm_with_introduced$mntd_standard by community_data_with_realm_with_introduced$introduced_species
W = 7925, p-value = 0.00001285
alternative hypothesis: true location shift is not equal to 0
wilcox.test(community_data_with_realm_with_introduced$beak_width_fdiv_standard~community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  community_data_with_realm_with_introduced$beak_width_fdiv_standard by community_data_with_realm_with_introduced$introduced_species
W = 8662, p-value = 0.0006884
alternative hypothesis: true location shift is not equal to 0
wilcox.test(community_data_with_realm_with_introduced$hwi_fdiv_standard~community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  community_data_with_realm_with_introduced$hwi_fdiv_standard by community_data_with_realm_with_introduced$introduced_species
W = 17606, p-value < 0.00000000000000022
alternative hypothesis: true location shift is not equal to 0
wilcox.test(community_data_with_realm_with_introduced$mass_fdiv_standard~community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  community_data_with_realm_with_introduced$mass_fdiv_standard by community_data_with_realm_with_introduced$introduced_species
W = 15028, p-value = 0.0000006706
alternative hypothesis: true location shift is not equal to 0

Invasive species per realm

Nearctic

test_required_values('Nearctic With Introduced', community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Nearctic' & community_data_with_realm_with_introduced$introduced_species,])
Nearctic With Introduced MNTD median 0.67 *
Nearctic With Introduced Beak Width FDiv median 0.29 
Nearctic With Introduced HWI FDiv median -0.8 ***
Nearctic With Introduced Mass FDiv median -0.26 
N 46

Neotropic

test_required_values('Neotropic With Introduced', community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Neotropic' & community_data_with_realm_with_introduced$introduced_species,])
Neotropic With Introduced MNTD median 0.03 
Neotropic With Introduced Beak Width FDiv median -0.44 ***
Neotropic With Introduced HWI FDiv median -0.31 
Neotropic With Introduced Mass FDiv median 0.33 *
N 64

Palearctic

palearctic_community_data_with_realm_with_introduced = community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Palearctic',]
test_required_values('Palearctic With Introduced', palearctic_community_data_with_realm_with_introduced[palearctic_community_data_with_realm_with_introduced$introduced_species,])
Palearctic With Introduced MNTD median 0.05 
Palearctic With Introduced Beak Width FDiv median 1.25 ***
Palearctic With Introduced HWI FDiv median -0.9 *
Palearctic With Introduced Mass FDiv median -0.01 
N 47
test_required_values('Palearctic Without Introduced', palearctic_community_data_with_realm_with_introduced[!palearctic_community_data_with_realm_with_introduced$introduced_species,])
Palearctic Without Introduced MNTD median 0.24 
Palearctic Without Introduced Beak Width FDiv median 1.24 *
Palearctic Without Introduced HWI FDiv median 0.79 
Palearctic Without Introduced Mass FDiv median 0.05 
N 25

MNTD

wilcox.test(palearctic_community_data_with_realm_with_introduced$mntd_standard~palearctic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  palearctic_community_data_with_realm_with_introduced$mntd_standard by palearctic_community_data_with_realm_with_introduced$introduced_species
W = 653, p-value = 0.4446
alternative hypothesis: true location shift is not equal to 0

Beak width

wilcox.test(palearctic_community_data_with_realm_with_introduced$beak_width_fdiv_standard~palearctic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  palearctic_community_data_with_realm_with_introduced$beak_width_fdiv_standard by palearctic_community_data_with_realm_with_introduced$introduced_species
W = 549, p-value = 0.6553
alternative hypothesis: true location shift is not equal to 0

HWI

wilcox.test(palearctic_community_data_with_realm_with_introduced$hwi_fdiv_standard~palearctic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  palearctic_community_data_with_realm_with_introduced$hwi_fdiv_standard by palearctic_community_data_with_realm_with_introduced$introduced_species
W = 874, p-value = 0.0005424
alternative hypothesis: true location shift is not equal to 0

Mass

wilcox.test(palearctic_community_data_with_realm_with_introduced$mass_fdiv_standard~palearctic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  palearctic_community_data_with_realm_with_introduced$mass_fdiv_standard by palearctic_community_data_with_realm_with_introduced$introduced_species
W = 637, p-value = 0.5647
alternative hypothesis: true location shift is not equal to 0

Afrotropic

afrotropic_community_data_with_realm_with_introduced = community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Afrotropic',]
test_required_values('Afrotropic With Introduced', afrotropic_community_data_with_realm_with_introduced[afrotropic_community_data_with_realm_with_introduced$introduced_species,])
Afrotropic With Introduced MNTD median -1.32 
Afrotropic With Introduced Beak Width FDiv median -1.01 
Afrotropic With Introduced HWI FDiv median 0.65 
Afrotropic With Introduced Mass FDiv median -0.85 
N 7
test_required_values('Afrotropic Without Introduced', afrotropic_community_data_with_realm_with_introduced[!afrotropic_community_data_with_realm_with_introduced$introduced_species,])
Afrotropic Without Introduced MNTD median -1.05 
Afrotropic Without Introduced Beak Width FDiv median -0.23 
Afrotropic Without Introduced HWI FDiv median -1.27 
Afrotropic Without Introduced Mass FDiv median -1.9 
N 2

MNTD

wilcox.test(afrotropic_community_data_with_realm_with_introduced$mntd_standard~afrotropic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  afrotropic_community_data_with_realm_with_introduced$mntd_standard by afrotropic_community_data_with_realm_with_introduced$introduced_species
W = 10, p-value = 0.5
alternative hypothesis: true location shift is not equal to 0

Beak width

wilcox.test(afrotropic_community_data_with_realm_with_introduced$beak_width_fdiv_standard~afrotropic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  afrotropic_community_data_with_realm_with_introduced$beak_width_fdiv_standard by afrotropic_community_data_with_realm_with_introduced$introduced_species
W = 10, p-value = 0.5
alternative hypothesis: true location shift is not equal to 0

HWI

wilcox.test(afrotropic_community_data_with_realm_with_introduced$hwi_fdiv_standard~afrotropic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  afrotropic_community_data_with_realm_with_introduced$hwi_fdiv_standard by afrotropic_community_data_with_realm_with_introduced$introduced_species
W = 2, p-value = 0.2222
alternative hypothesis: true location shift is not equal to 0

Mass

wilcox.test(afrotropic_community_data_with_realm_with_introduced$mass_fdiv_standard~afrotropic_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum exact test

data:  afrotropic_community_data_with_realm_with_introduced$mass_fdiv_standard by afrotropic_community_data_with_realm_with_introduced$introduced_species
W = 0, p-value = 0.05556
alternative hypothesis: true location shift is not equal to 0

Indomalayan

indomalayan_community_data_with_realm_with_introduced = community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Indomalayan',]
test_required_values('Indomalayan With Introduced', indomalayan_community_data_with_realm_with_introduced[indomalayan_community_data_with_realm_with_introduced$introduced_species,])
Indomalayan With Introduced MNTD median -0.7 ***
Indomalayan With Introduced Beak Width FDiv median 0.06 
Indomalayan With Introduced HWI FDiv median 1.25 ***
Indomalayan With Introduced Mass FDiv median -0.08 
N 19
test_required_values('Indomalayan Without Introduced', indomalayan_community_data_with_realm_with_introduced[!indomalayan_community_data_with_realm_with_introduced$introduced_species,])
Indomalayan Without Introduced MNTD median -0.61 ***
Indomalayan Without Introduced Beak Width FDiv median -0.9 ***
Indomalayan Without Introduced HWI FDiv median 1.11 ***
Indomalayan Without Introduced Mass FDiv median 0.92 ***
N 92

MNTD

wilcox.test(indomalayan_community_data_with_realm_with_introduced$mntd_standard~indomalayan_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  indomalayan_community_data_with_realm_with_introduced$mntd_standard by indomalayan_community_data_with_realm_with_introduced$introduced_species
W = 1098, p-value = 0.08015
alternative hypothesis: true location shift is not equal to 0

Beak width

wilcox.test(indomalayan_community_data_with_realm_with_introduced$beak_width_fdiv_standard~indomalayan_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  indomalayan_community_data_with_realm_with_introduced$beak_width_fdiv_standard by indomalayan_community_data_with_realm_with_introduced$introduced_species
W = 430, p-value = 0.0005162
alternative hypothesis: true location shift is not equal to 0

HWI

wilcox.test(indomalayan_community_data_with_realm_with_introduced$hwi_fdiv_standard~indomalayan_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  indomalayan_community_data_with_realm_with_introduced$hwi_fdiv_standard by indomalayan_community_data_with_realm_with_introduced$introduced_species
W = 743, p-value = 0.3069
alternative hypothesis: true location shift is not equal to 0

Mass

wilcox.test(indomalayan_community_data_with_realm_with_introduced$mass_fdiv_standard~indomalayan_community_data_with_realm_with_introduced$introduced_species)

    Wilcoxon rank sum test with continuity correction

data:  indomalayan_community_data_with_realm_with_introduced$mass_fdiv_standard by indomalayan_community_data_with_realm_with_introduced$introduced_species
W = 1401, p-value = 0.00003756
alternative hypothesis: true location shift is not equal to 0

Australasia

test_required_values('Australasia With Introduced', community_data_with_realm_with_introduced[community_data_with_realm_with_introduced$core_realm == 'Australasia' & community_data_with_realm_with_introduced$introduced_species,])
Australasia With Introduced MNTD median -1.39 
Australasia With Introduced Beak Width FDiv median -0.75 
Australasia With Introduced HWI FDiv median 0.77 
Australasia With Introduced Mass FDiv median -0.96 
N 6

What families exist in which realms?

communities %>% 
  left_join(city_to_realm) %>% 
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family, core_realm) %>%
  distinct() %>%
  arrange(core_realm)
Joining with `by = join_by(city_id)`

Total urban families

communities %>% 
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family) %>%
  distinct() %>%
  arrange()

of which urban

communities %>% 
  filter(relative_abundance_proxy > 0) %>%
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family) %>%
  distinct() %>%
  arrange()

Summary metrics by introduced species

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
city_introduced_species = communities %>% group_by(city_id) %>% summarise(number_of_species = n()) %>% left_join(
  communities %>% group_by(city_id) %>% filter(origin == 'Introduced') %>% summarise(number_of_introduced_species = n())
) %>% replace_na(list(number_of_introduced_species = 0))
Joining with `by = join_by(city_id)`
community_data_with_introductions = left_join(community_data, city_introduced_species)
Joining with `by = join_by(city_id)`
community_data_with_introductions$has_introduced_species = community_data_with_introductions$number_of_introduced_species > 0
community_data_with_introductions
communities %>% 
  filter(origin == 'Introduced') %>%
  dplyr::select(ebird_species_name) %>%
  group_by(ebird_species_name) %>%
  summarise(total_cities = n()) %>%
  arrange(desc(total_cities))
community_data_with_introductions[,c('mntd_standard', 'has_introduced_species')]
community_data_with_introductions %>% group_by(has_introduced_species) %>% summarise(
  total_cities = n(), 
  
  mean_mntd_std = mean(mntd_standard, na.rm = T),
  median_mntd_std = median(mntd_standard, na.rm = T),
  sd_mntd_std = sd(mntd_standard, na.rm = T),
  
  mean_mass_fdiv_std = mean(mass_fdiv_standard, na.rm = T),
  median_mass_fdiv_std = median(mass_fdiv_standard, na.rm = T),
  sd_mass_fdiv_std = sd(mass_fdiv_standard, na.rm = T),
  
  mean_gape_width_fdiv_std = mean(beak_width_fdiv_standard, na.rm = T),
  median_gape_width_fdiv_std = median(beak_width_fdiv_standard, na.rm = T),
  sd_gape_width_fdiv_std = sd(beak_width_fdiv_standard, na.rm = T),
  
  mean_handwing_index_fdiv_std = mean(hwi_fdiv_standard, na.rm = T),
  median_handwing_index_fdiv_std = median(hwi_fdiv_standard, na.rm = T),
  sd_handwing_index_fdiv_std = sd(hwi_fdiv_standard, na.rm = T)
)

MNTD

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mntd_standard)) + geom_boxplot()

wilcox.test(mntd_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mntd_standard by has_introduced_species
W = 7925, p-value = 0.00001285
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.53±0.27) and those without (0.47±0.19) (p-value = 0.02).

Mass FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mass_fdiv_standard)) + geom_boxplot()

wilcox.test(mass_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mass_fdiv_standard by has_introduced_species
W = 15028, p-value = 0.0000006706
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.57±0.27) and those without (0.73±0.24) (p < 0.0001)

Beak Gape FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = beak_width_fdiv_standard)) + geom_boxplot()

wilcox.test(beak_width_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  beak_width_fdiv_standard by has_introduced_species
W = 8662, p-value = 0.0006884
alternative hypothesis: true location shift is not equal to 0

There is NOT a significant difference between the response of cities with introduced species (0.61±0.30) and those without (0.56±0.27)

HWI FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = hwi_fdiv_standard)) + geom_boxplot()

wilcox.test(hwi_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  hwi_fdiv_standard by has_introduced_species
W = 17606, p-value < 0.00000000000000022
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.49±0.30) and those without (0.79±0.21) (p < 0.0001)

What proportion of cities in each realm have introduced species

community_data_with_introductions %>% left_join(city_to_realm) %>%
  group_by(core_realm) %>%
  summarise(
    total_cities = n(), 
    total_cities_with_introduced = sum(has_introduced_species), 
    total_cities_without_introduced = n() - sum(has_introduced_species)) %>%
  arrange(core_realm)
Joining with `by = join_by(city_id)`

Are any introduced species not also present in a city?

communities %>% 
  filter(origin == 'Introduced') %>% 
  filter(relative_abundance_proxy < 0.1)

What’s the average relative abundance of introduced species compared to native

communities %>% 
  group_by(origin) %>% 
  summarise(average_relative_abundance = mean(relative_abundance_proxy))
communities %>% 
  group_by(origin) %>% 
  filter(relative_abundance_proxy > 0) %>%
  summarise(average_relative_abundance = mean(relative_abundance_proxy))
communities %>% 
  group_by(origin) %>% 
  summarise(average_relative_abundance = mean(relative_abundance_proxy))

Create analysis data frame

geography = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'geography.csv'))
Rows: 342 Columns: 26── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (26): city_id, city_avg_ndvi, city_avg_elevation, city_avg_temp, city_avg_min_monthly_temp, city_avg_max_monthly_temp, city_avg_monthly_temp, city_avg_rainfall, city_av...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
names(geography)
 [1] "city_id"                       "city_avg_ndvi"                 "city_avg_elevation"            "city_avg_temp"                 "city_avg_min_monthly_temp"    
 [6] "city_avg_max_monthly_temp"     "city_avg_monthly_temp"         "city_avg_rainfall"             "city_avg_max_monthly_rainfall" "city_avg_min_monthly_rainfall"
[11] "city_avg_soil_moisture"        "city_max_elev"                 "city_min_elev"                 "city_elev_range"               "region_20km_avg_ndvi"         
[16] "region_20km_avg_elevation"     "region_20km_avg_soil_moisture" "region_20km_max_elev"          "region_20km_min_elev"          "region_20km_elev_range"       
[21] "region_50km_avg_ndvi"          "region_50km_avg_elevation"     "region_50km_avg_soil_moisture" "region_50km_max_elev"          "region_50km_min_elev"         
[26] "region_50km_elev_range"       
analysis_data = community_data_with_realm[,c('city_id', 'mntd_standard', 'mass_fdiv_standard', 'beak_width_fdiv_standard', 'hwi_fdiv_standard', 'core_realm')] %>% 
  left_join(city_points[,c('city_id', 'latitude', 'longitude')]) %>%
  left_join(community_data_with_introductions[,c('city_id', 'has_introduced_species')]) %>%
  left_join(geography) %>%
  left_join(spatial_var)
Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`
analysis_data$abs_latitude = abs(analysis_data$latitude)
analysis_data$core_realm = factor(analysis_data$core_realm, levels = c('Palearctic', 'Nearctic', 'Neotropic', 'Afrotropic', 'Indomalayan', 'Australasia', 'Oceania'))
analysis_data$has_introduced_species = factor(analysis_data$has_introduced_species, level = c('FALSE', 'TRUE'), labels = c('No introduced species', 'Introduced species'))
model_data = function(df, dependant_var) {
  df[,c(dependant_var, 'core_realm', 'abs_latitude', 'longitude', 'has_introduced_species', 'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp', 'city_avg_min_monthly_temp', 'city_avg_max_monthly_temp', 'city_avg_monthly_temp', 'city_avg_rainfall', 'city_avg_max_monthly_rainfall', 'city_avg_min_monthly_rainfall', 'city_avg_soil_moisture', 'city_max_elev', 'city_min_elev', 'city_elev_range', 'region_20km_avg_ndvi', 'region_20km_avg_elevation', 'region_20km_avg_soil_moisture', 'region_20km_max_elev', 'region_20km_min_elev', 'region_20km_elev_range', 'region_50km_avg_ndvi', 'region_50km_avg_elevation', 'region_50km_avg_soil_moisture', 'region_50km_max_elev', 'region_50km_min_elev', 'region_50km_elev_range')]
}
model_data(analysis_data, 'mntd_standard')
names(analysis_data)
 [1] "city_id"                       "mntd_standard"                 "mass_fdiv_standard"            "beak_width_fdiv_standard"      "hwi_fdiv_standard"            
 [6] "core_realm"                    "latitude"                      "longitude"                     "geometry"                      "has_introduced_species"       
[11] "city_avg_ndvi"                 "city_avg_elevation"            "city_avg_temp"                 "city_avg_min_monthly_temp"     "city_avg_max_monthly_temp"    
[16] "city_avg_monthly_temp"         "city_avg_rainfall"             "city_avg_max_monthly_rainfall" "city_avg_min_monthly_rainfall" "city_avg_soil_moisture"       
[21] "city_max_elev"                 "city_min_elev"                 "city_elev_range"               "region_20km_avg_ndvi"          "region_20km_avg_elevation"    
[26] "region_20km_avg_soil_moisture" "region_20km_max_elev"          "region_20km_min_elev"          "region_20km_elev_range"        "region_50km_avg_ndvi"         
[31] "region_50km_avg_elevation"     "region_50km_avg_soil_moisture" "region_50km_max_elev"          "region_50km_min_elev"          "region_50km_elev_range"       
[36] "NMDS1"                         "NMDS2"                         "abs_latitude"                 

NMDS Spatial Helpers

analysis_data_nmds_coords = analysis_data[,c('NMDS1', 'NMDS2')]
coordinates(analysis_data_nmds_coords)  = ~ NMDS1 + NMDS2

analysis_data_nmds_nearneigh <- knearneigh(analysis_data_nmds_coords)
Warning: knearneigh: identical points foundWarning: knearneigh: kd_tree not available for identical points
analysis_data_nmds_neighbours <- knn2nb(analysis_data_nmds_nearneigh)
Warning: neighbour object has 76 sub-graphs

Polygons around realms in NMDS plot

cities_to_realms_nmds = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv')) %>% left_join(analysis_data) %>% filter(!is.na(NMDS1))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id, core_realm)`
unique(cities_to_realms_nmds$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic"  "Indomalayan" "Australasia"
realm_nmds_neartic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Nearctic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_neotropic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Neotropic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_palearctic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Palearctic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_afrotropic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Afrotropic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_indomalayan_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Indomalayan') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_australasia_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Australasia') %>% slice(chull(NMDS1, NMDS2))

polygon_line_type = 'dashed'
polygon_linewidth = 0.4

with_realms_nmds = function(g) {
  g + 
    geom_polygon(data = realm_nmds_neartic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_neotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_palearctic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_afrotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_indomalayan_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_australasia_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0)
}

Lat Long Spatial Helpers

analysis_data_latlong_coords = analysis_data[,c('longitude', 'latitude')]
coordinates(analysis_data_latlong_coords)  = ~ longitude + latitude

analysis_data_coords_nearneigh <- knearneigh(analysis_data_latlong_coords, longlat = TRUE)
analysis_data_neighbours <- knn2nb(analysis_data_coords_nearneigh)
Warning: neighbour object has 97 sub-graphs

Polygons around realms in lat long plot

cities_to_realms_latlong = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv')) %>% left_join(analysis_data) %>% filter(!is.na(latitude))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id, core_realm)`
unique(cities_to_realms_latlong$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic"  "Indomalayan" "Australasia"
realm_latlong_neartic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Nearctic') %>% slice(chull(latitude, longitude))
realm_latlong_neotropic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Neotropic') %>% slice(chull(latitude, longitude))
realm_latlong_palearctic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Palearctic') %>% slice(chull(latitude, longitude))
realm_latlong_afrotropic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Afrotropic') %>% slice(chull(latitude, longitude))
realm_latlong_indomalayan_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Indomalayan') %>% slice(chull(latitude, longitude))
realm_latlong_australasia_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Australasia') %>% slice(chull(latitude, longitude))

with_realms_latlong = function(g) {
  g + 
    geom_polygon(data = realm_latlong_neartic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_neotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_palearctic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_afrotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_indomalayan_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_australasia_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0)
}

Check for spatial autocorrelation

MNTD

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mntd_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mntd_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.3361, p-value = 0.0000000000001099
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.529289710      -0.003257329       0.005269629 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mntd_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mntd_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.5062, p-value = 0.00000000003854
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.444629830      -0.003257329       0.004738970 

FDiv Beak Width

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = beak_width_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$beak_width_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.754, p-value = 0.000000000000004451
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.560232185      -0.003257329       0.005281006 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = beak_width_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$beak_width_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 8.0778, p-value = 0.0000000000000003298
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.553407999      -0.003257329       0.004749010 

FDiv HWI

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$hwi_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 9.7654, p-value < 0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.706636768      -0.003257329       0.005284486 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$hwi_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 5.7044, p-value = 0.000000005839
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.389975146      -0.003257329       0.004752081 

FDiv Mass

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mass_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mass_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 9.159, p-value < 0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.661853721      -0.003257329       0.005273363 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mass_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mass_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.2152, p-value = 0.0000000002563
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.424748274      -0.003257329       0.004742265 

Examine individual metrics

all_explanatories = c(
    'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp',
    'region_50km_avg_soil_moisture',
    'core_realmAfrotropic', 'core_realmAustralasia', 'core_realmIndomalayan', 'core_realmNearctic', 'core_realmNeotropic', 'core_realmPalearctic',
    'has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species'
)

all_explanatory_names = factor(
   c(
    'Avg. NDVI', 'Avg. Elevation', 'Avg. Temp.',
    'Avg. Soil Moisture',
    'Afrotropic', 'Australasia', 'Indomalayan', 'Nearctic', 'Neotropic', 'Palearctic',
    'Introduced Absent', 'Introduced Present'
  ), ordered = T
)

explanatory_dictionary = data.frame(explanatory = all_explanatories, explanatory_name = all_explanatory_names)
  
with_explanatory_type_labels = function(p) {
  p = p[p$explanatory != '(Intercept)',]
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Realm'
  p$type[p$explanatory %in% c('city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp')] <- 'City geography'
  p$type[p$explanatory %in% c('region_50km_avg_soil_moisture')] <- 'Regional (50 km) geography'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species')] <- 'Introduced species'
  p
}

with_explanatory_names = function(p) {
  p %>% left_join(explanatory_dictionary) %>% arrange(desc(explanatory_name))
}

type_labels = function(p) {
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Realm'
  p$type[p$explanatory %in% c('city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp', 'city_avg_min_monthly_temp', 'city_avg_max_monthly_temp', 
    'city_avg_monthly_temp', 'city_avg_rainfall', 'city_avg_max_monthly_rainfall', 'city_avg_min_monthly_rainfall', 
    'city_avg_soil_moisture', 'city_max_elev', 'city_min_elev', 'city_elev_range')] <- 'City geography'
  p$type[p$explanatory %in% c('region_50km_avg_ndvi', 'region_50km_avg_elevation', 'region_50km_avg_soil_moisture', 'region_50km_max_elev', 
    'region_50km_min_elev', 'region_50km_elev_range')] <- 'Regional (50 km) geography'
   p$type[p$explanatory %in% c('region_20km_avg_ndvi', 'region_20km_avg_elevation', 'region_20km_avg_soil_moisture', 'region_20km_max_elev', 
    'region_20km_min_elev', 'region_20km_elev_range')] <- 'Regional (20 km) geography'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species')] <- 'Introduced species'
  p
}
explanatory_labels = c(
  'has_introduced_species'='Introduced species', 
  'has_introduced_speciesNo introduced species'='Introduced absent', 
  'has_introduced_speciesIntroduced species'='Introduced present',
  'city_avg_ndvi'='Average NDVI', 
  'city_avg_elevation'='Average elevation', 
  'city_avg_temp'='Average temperature', 
  'city_avg_min_monthly_temp'='Average minimum monthly temperature', 
  'city_avg_max_monthly_temp'='Average maximum monthly temperature', 
  'city_avg_monthly_temp'='Average monthly temperature', 
  'city_avg_rainfall'='Average rainfall', 
  'city_avg_max_monthly_rainfall'='Average maximum monthly rainfall', 
  'city_avg_min_monthly_rainfall'='Average minimum monthly rainfall', 
  'city_avg_soil_moisture'='Average soil moisture', 
  'city_max_elev'='Maximum elevation', 
  'city_min_elev'='Minimum elevation', 
  'city_elev_range'='Elevation range', 
  'region_20km_avg_ndvi'='Average NDVI', 
  'region_20km_avg_elevation'='Average elevation', 
  'region_20km_avg_soil_moisture'='Average soil moisture', 
  'region_20km_max_elev'='Maximum elevation', 
  'region_20km_min_elev'='Minimum elevation',
  'region_20km_elev_range'='Elevation range',
  'region_50km_avg_ndvi'='Average NDVI',
  'region_50km_avg_elevation'='Average elevation',
  'region_50km_avg_soil_moisture'='Average soil moisture', 
  'region_50km_max_elev'='Maximum elevation',
  'region_50km_min_elev'='Minimum elevation', 
  'region_50km_elev_range'='Elevation range',
  'abs_latitude' = 'Absolute latitude',
  'latitude' = 'Latitude',
  'longitude' = 'Longitude',
  'core_realmAfrotropic' = 'Afrotropical', 
  'core_realmAustralasia' = 'Austaliasian', 
  'core_realmIndomalayan' = 'Indomalayan', 
  'core_realmNearctic' = 'Nearctic', 
  'core_realmNeotropic' = 'Neotropical',
  'core_realmPalearctic' = 'Palearctic',
  'core_realmOceania' = 'Oceanical')
create_formula = function(response_var) {
  as.formula(paste(response_var, '~ core_realm + city_avg_ndvi + city_avg_elevation + city_avg_temp + region_50km_avg_soil_moisture + has_introduced_species'))
}

Helper plot functions

geom_map = function(map_sf, title) {
  norm_mntd_analysis_geo = ggplot() + 
    geom_sf(data = world_map, aes(geometry = geometry)) +
    map_sf +
    standardised_colours_scale +
    labs(colour = 'Standardised\nResponse') +
    theme_bw() +
    theme(legend.position="bottom")
}

Helper Dredge functions

# Taken from MuMIN package
# https://rdrr.io/cran/MuMIn/src/R/averaging.R
# https://rdrr.io/cran/MuMIn/src/R/model.avg.R

.coefarr.avg <-
  function(cfarr, weight, revised.var, full, alpha) {   
    weight <- weight / sum(weight)
    nCoef <- dim(cfarr)[3L]
    if(full) {
      nas <- is.na(cfarr[, 1L, ]) & is.na(cfarr[, 2L, ])
      cfarr[, 1L, ][nas] <- cfarr[, 2L, ][nas] <- 0
      #cfarr[, 1L:2L, ][is.na(cfarr[, 1L:2L, ])] <- 0
      if(!all(is.na(cfarr[, 3L, ])))
        cfarr[ ,3L, ][is.na(cfarr[ , 3L, ])] <- Inf
    }
    
    avgcoef <- array(dim = c(nCoef, 5L),
                     dimnames = list(dimnames(cfarr)[[3L]], c("Estimate",
                                                              "Std. Error", "Adjusted SE", "Lower CI", "Upper CI")))
    for(i in seq_len(nCoef))
      avgcoef[i, ] <- par.avg(cfarr[, 1L, i], cfarr[, 2L, i], weight,
                              df = cfarr[, 3L, i], alpha = alpha, revised.var = revised.var)
    
    avgcoef[is.nan(avgcoef)] <- NA
    return(avgcoef)
  }

.makecoefmat <- function(cf) {
  no.ase <- all(is.na(cf[, 3L]))
  z <- abs(cf[, 1L] / cf[, if(no.ase) 2L else 3L])
  pval <- 2 * pnorm(z, lower.tail = FALSE)
  cbind(cf[, if(no.ase) 1L:2L else 1L:3L, drop = FALSE],
        `z value` = z, `Pr(>|z|)` = zapsmall(pval))
}

# Generate model selections using lmer, dredge, and model.avg
# `forumla` : a two-sided linear formula object describing both the fixed-effects and random-effects part of the model
# `data` : the data frame containing the variables from the formula
# `aic_delta` : the AIC delta to use for selecting models in model average
model_average <- function(formula, data, aic_delta = 20) {
  model <- lm(
    formula,
    data=data
  )
  dredge_result <- dredge(model)
  summary(model.avg(dredge_result, subset = delta < aic_delta))
}

# Create a summary data frame containing the selected variables from a model
# `model_sum` : The model summary output from `model_average`
model_summary <- function(model_sum) {
  .column_name <- function(postfix) {
    postfix
  }
  
  # just return the estimate and p value
  weight <- model_sum$msTable[, 5L]
  
  coefmat.full <- as.data.frame(.makecoefmat(.coefarr.avg(model_sum$coefArray, weight,
                                                          attr(model_sum, "revised.var"), TRUE, 0.05)))
  
  coefmat.subset <-
    as.data.frame(.makecoefmat(.coefarr.avg(model_sum$coefArray, weight,
                                            attr(model_sum, "revised.var"), FALSE, 0.05)))
  
  
  coefmat.subset <- coefmat.subset[-c(1), c(1, 2, 5)]
  names(coefmat.subset) <- c(.column_name("estimate"), .column_name("error"), .column_name("p"))
  coefmat.subset <- tibble::rownames_to_column(coefmat.subset, "explanatory")
  coefmat.subset$model = 'subset'
  
  coefmat.full <- coefmat.full[-c(1), c(1, 2, 5)]
  names(coefmat.full) <- c(.column_name("estimate"), .column_name("error"), .column_name("p"))
  coefmat.full <- tibble::rownames_to_column(coefmat.full, "explanatory")
  coefmat.full$model = 'full'
  
  rbind(coefmat.full, coefmat.subset)
}
plot_dredge_result = function(result_table) {
  p = result_table[result_table$model == 'full',]
  p = type_labels(p)

  ggplot(p, aes(y = explanatory, x = estimate, colour = type)) + 
    geom_line() +
    geom_point() +
    geom_errorbar(aes(xmin=estimate-error, xmax=estimate+error), width=.2,
                   position=position_dodge(0.05)) +
    scale_y_discrete(
      limits = rev(levels(p$explanatory)), 
      labels = explanatory_labels) +
    scale_colour_manual(
      values = c(realm_colour, city_geography_colour, regional_50km_geography_colour, regional_20km_geography_colour, introduced_species_colour), 
      breaks = c('Realm', 'City geography', 'Regional (50 km) geography', 'Regional (20 km) geography', 'Introduced species')) +
    theme_bw() +
    geom_vline(xintercept=0, linetype="dotted") +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') + ylab('Predictor') +
    theme(legend.justification = "top")
}

GLS Spatial Helpers

gls_method = "ML"

spatial_model = function(formula, correlation) {
  gls(
    formula, 
    data = analysis_data, 
    correlation = correlation, 
    method = gls_method
  )
}

plot_spatial_result = function(model_result) {
  model_summary = summary(model_result)
  result_table = as.data.frame(model_summary$tTable)
  result_table$explanatory = rownames(result_table)
  
  result_table = result_table %>% with_explanatory_type_labels() %>% with_explanatory_names()
  
  ggplot2::ggplot(result_table, ggplot2::aes(y=factor(explanatory_name, level = all_explanatory_names, ordered = T), x=Value, colour = type)) + 
    ggplot2::geom_line() +
    ggplot2::geom_point() +
    ggplot2::geom_errorbar(ggplot2::aes(xmin=Value-Std.Error, xmax=Value+Std.Error), width=.2,
                   position=ggplot2::position_dodge(0.05)) +
    ggplot2::theme_bw() +
    ggplot2::geom_vline(xintercept=0, linetype="dotted") +
    ggplot2::theme(legend.justification = "top") +
    ylab('Predictor') +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') +
    scale_colour_manual(
      values = c(realm_colour, city_geography_colour, regional_50km_geography_colour, introduced_species_colour), 
      breaks = c('Realm', 'City geography', 'Regional (50 km) geography', 'Introduced species')) +
    scale_y_discrete(limits = rev(all_explanatory_names[all_explanatory_names %in% result_table$explanatory_name]))
}

Choose best spatial correlation function

AIC(spatial_model(create_formula('mntd_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 662.3448
AIC(spatial_model(create_formula('mntd_standard'), corLin(form = ~ latitude + longitude)))
[1] 668.183
AIC(spatial_model(create_formula('mntd_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 653.2191
AIC(spatial_model(create_formula('mntd_standard'), corExp(form = ~ latitude + longitude)))
[1] 656.8138
AIC(spatial_model(create_formula('mntd_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 658.2282
AIC(spatial_model(create_formula('mntd_standard'), corGaus(form = ~ latitude + longitude)))
[1] 662.8906
AIC(spatial_model(create_formula('mntd_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 650.6309
AIC(spatial_model(create_formula('mntd_standard'), corRatio(form = ~ latitude + longitude)))
[1] 655.0259
AIC(spatial_model(create_formula('mntd_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 660.0803
AIC(spatial_model(create_formula('mntd_standard'), corSpher(form = ~ latitude + longitude)))
[1] 664.3069

MNTD: corRatio with NMDS + lat/long

AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 793.8812
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corLin(form = ~ latitude + longitude)))
[1] 793.8812
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 789.9986
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 790.7424
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 794.1143
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 794.1127
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 791.6815
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 791.833
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 794.1625
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 794.1625

Beak width: corExp with NMDS + lat/long

AIC(spatial_model(create_formula('hwi_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 695.3483
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corLin(form = ~ latitude + longitude)))
[1] 699.9416
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 667.3598
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 672.0891
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 705.4322
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 709.9593
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 686.368
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 691.6763
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 690.2192
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 696.1892

HWI: corExp with NMDS + lat/long

AIC(spatial_model(create_formula('mass_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 777.7153
AIC(spatial_model(create_formula('mass_fdiv_standard'), corLin(form = ~ latitude + longitude)))
[1] 776.4599
AIC(spatial_model(create_formula('mass_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 752.6284
AIC(spatial_model(create_formula('mass_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 755.2302
AIC(spatial_model(create_formula('mass_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 786.4347
AIC(spatial_model(create_formula('mass_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 784.9074
AIC(spatial_model(create_formula('mass_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 766.6933
AIC(spatial_model(create_formula('mass_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 766.7129
AIC(spatial_model(create_formula('mass_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 780.5551
AIC(spatial_model(create_formula('mass_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 780.1225

Mass: corExp with NMDS + lat/long

correlation_formula = as.formula('~ NMDS1 + NMDS2 + latitude + longitude')

correlation_function_fdiv = function() {
  corExp(form = correlation_formula)
}

correlation_function_mntd = function() {
  corRatio(form = correlation_formula)
}

MNTD

std_mntd_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = mntd_standard, geometry = geometry)), 'MNTD')
std_mntd_analysis_geo_plot

std_mntd_analysis_data = model_data(analysis_data[!is.na(analysis_data$mntd_standard),], 'mntd_standard')
std_mntd_analysis_formula = create_formula('mntd_standard')
std_mntd_analysis_result <- model_average(std_mntd_analysis_formula, std_mntd_analysis_data)
Fixed term is "(Intercept)"
std_mntd_analysis_result_table = model_summary(std_mntd_analysis_result)
std_mntd_analysis_result_table
std_mntd_analysis_pred_plot = plot_dredge_result(std_mntd_analysis_result_table)
std_mntd_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

std_mntd_lm = lm(std_mntd_analysis_formula, std_mntd_analysis_data)
moran.test(std_mntd_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mntd_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.8174, p-value = 0.00006743
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.273851651      -0.003257329       0.005269469 
moran.test(std_mntd_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mntd_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 2.9313, p-value = 0.001688
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.198532827      -0.003257329       0.004738829 
mntd_spatial_model = spatial_model(std_mntd_analysis_formula, correlation_function_mntd())
moran.test(mntd_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.0185, p-value = 0.00002929
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.288487485      -0.003257329       0.005270882 
moran.test(mntd_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.1254, p-value = 0.0008877
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.211923502      -0.003257329       0.004740076 
std_mntd_analysis_pred_spatial_plot = plot_spatial_result(mntd_spatial_model)
Joining with `by = join_by(explanatory)`
std_mntd_analysis_pred_spatial_plot

Gape width - FDiv

std_gape_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = beak_width_fdiv_standard, geometry = geometry)), 'Beak Width FDiv')
std_gape_fdiv_analysis_geo_plot

std_gape_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$beak_width_fdiv_standard),], 'beak_width_fdiv_standard')
std_gape_fdiv_analysis_formula = create_formula('beak_width_fdiv_standard')
std_gape_fdiv_analysis_result <- model_average(std_gape_fdiv_analysis_formula, std_gape_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_gape_fdiv_analysis_result_table = model_summary(std_gape_fdiv_analysis_result)
std_gape_fdiv_analysis_result_table
std_gape_fdiv_analysis_pred_plot = plot_dredge_result(std_gape_fdiv_analysis_result_table)
std_gape_fdiv_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

std_gape_fdiv_lm = lm(std_gape_fdiv_analysis_formula, std_gape_fdiv_analysis_data)
moran.test(std_gape_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.55, p-value = 0.0001926
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.254212474      -0.003257329       0.005260213 
moran.test(std_gape_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.6631, p-value = 0.04815
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.111128951      -0.003257329       0.004730661 
std_gape_fdiv_spatial_model = spatial_model(std_gape_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.6108, p-value = 0.0001526
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.258639556      -0.003257329       0.005260832 
moran.test(std_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.8619, p-value = 0.03131
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.124813501      -0.003257329       0.004731207 
std_gape_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_gape_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_gape_fdiv_analysis_pred_spatial_plot

HWI - FDiv

std_hwi_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = hwi_fdiv_standard, geometry = geometry)), 'HWI FDiv')
std_hwi_fdiv_analysis_geo_plot

std_hwi_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$hwi_fdiv_standard),], 'hwi_fdiv_standard')
std_hwi_fdiv_analysis_formula = create_formula('hwi_fdiv_standard')
std_hwi_fdiv_analysis_result <- model_average(std_hwi_fdiv_analysis_formula, std_hwi_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_hwi_fdiv_analysis_result_table = model_summary(std_hwi_fdiv_analysis_result)
std_hwi_fdiv_analysis_result_table
std_hwi_fdiv_analysis_pred_plot = plot_dredge_result(std_hwi_fdiv_analysis_result_table)
std_hwi_fdiv_analysis_pred_plot

std_hwi_fdiv_lm = lm(std_hwi_fdiv_analysis_formula, std_hwi_fdiv_analysis_data)
moran.test(std_hwi_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 6.197, p-value = 0.0000000002877
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.445978540      -0.003257329       0.005255093 
moran.test(std_hwi_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.7792, p-value = 0.00007868
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.256549224      -0.003257329       0.004726143 
std_hwi_fdiv_spatial_model = spatial_model(std_hwi_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.2343, p-value = 0.000000000000234
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.521468855      -0.003257329       0.005261042 
moran.test(std_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.3574, p-value = 0.0003935
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.227679202      -0.003257329       0.004731393 
std_hwi_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_hwi_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_hwi_fdiv_analysis_pred_spatial_plot

Mass - FDiv

std_mass_fdiv_analysis_geo_plot = geom_map(geom_sf(data = analysis_data, aes(color = mass_fdiv_standard, geometry = geometry)), 'Mass FDiv')
std_mass_fdiv_analysis_geo_plot

std_mass_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$mass_fdiv_standard),], 'mass_fdiv_standard')
std_mass_fdiv_analysis_formula = create_formula('mass_fdiv_standard')
std_mass_fdiv_analysis_result <- model_average(std_mass_fdiv_analysis_formula, std_mass_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_mass_fdiv_analysis_result_table = model_summary(std_mass_fdiv_analysis_result)
std_mass_fdiv_analysis_result_table
std_mass_fdiv_analysis_pred_plot = plot_dredge_result(std_mass_fdiv_analysis_result_table)
std_mass_fdiv_analysis_pred_plot

std_mass_fdiv_lm = lm(std_mass_fdiv_analysis_formula, std_mass_fdiv_analysis_data)
moran.test(std_mass_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.4464, p-value = 0.00000000000004797
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.536458696      -0.003257329       0.005253399 
moran.test(std_mass_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 4.7703, p-value = 0.0000009196
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.324636672      -0.003257329       0.004724648 
std_mass_fdiv_spatial_model = spatial_model(std_mass_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.9834, p-value = 0.0000000000000007117
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.575429652      -0.003257329       0.005254232 
moran.test(std_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 5.2794, p-value = 0.00000006481
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.359654511      -0.003257329       0.004725382 
std_mass_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_mass_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_mass_fdiv_analysis_pred_spatial_plot

Create plot of differences in process response

pred_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_hwi_fdiv_analysis_pred_plot + theme(legend.box.margin = margin(0, 0, 0, 0)) + guides(colour=guide_legend(ncol=2)) + labs(color = "Predictor type")
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
geo_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_mass_fdiv_analysis_geo_plot + theme(legend.box.margin = margin(-80, 0, 0, 12), legend.title.position = "top", legend.key.width = unit(10, 'mm')) + labs(color = "Standardised response")
)

legend = plot_grid(
  geo_legend,
  pred_legend, 
  nrow = 1
)
legend

plot_grid(
  plot_grid(
    std_mntd_analysis_geo_plot + theme(legend.position="none"), 
    std_mntd_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("MNTD", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_gape_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_gape_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Beak Width", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_hwi_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_hwi_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("HWI", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_mass_fdiv_analysis_geo_plot + theme(legend.position="none"), 
    std_mass_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Mass", size = 16, angle = 90, x = 0.01, y = 0.5), 
  legend,
  nrow = 5
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
ggsave(filename(FIGURES_OUTPUT_DIR, 'process_response.jpg'), width = 3000, height = 3200, units = 'px')

pred_fig_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_hwi_fdiv_analysis_pred_plot + theme(legend.box.margin = margin(0, 0, 0, -20)) + guides(colour=guide_legend(ncol=2)) + labs(color = "Predictor type")
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
geo_fig_legend <- ggpubr::get_legend(
  # create some space to the left of the legend
  std_mass_fdiv_analysis_geo_plot + theme(legend.box.margin = margin(0, 0, 0, 0), legend.title.position = "top", legend.key.width = unit(10, 'mm')) + labs(color = "Standardised response")
)

remove_x_scale =  scale_x_continuous(name = '', limits = c(-3, 3))
theme_no_legend = theme(legend.position="none", panel.border = element_blank(), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.line = element_line(colour = "black"))

fig2 = grid.arrange(
  # row 1 - titles
  arrangeGrob(grid::textGrob('A) Standardised response by city', x = 0.1, hjust = 0, gp=gpar(fontface="bold"))),
  arrangeGrob(grid::textGrob('B) Standardised response predictors', x = 0.1, hjust = 0, gp=gpar(fontface="bold"))),
  # row 2
  arrangeGrob(
    std_mntd_analysis_geo_plot + theme_no_legend, 
    left = "MNTD"
  ),
  arrangeGrob(
    std_mntd_analysis_pred_plot + theme_no_legend + remove_x_scale + ylab('')
  ),
  # row 3
  arrangeGrob(std_gape_fdiv_analysis_geo_plot + theme_no_legend, left = "Beak Width"),
  arrangeGrob(
    std_gape_fdiv_analysis_pred_plot + theme_no_legend + remove_x_scale + ylab('')
  ),
  # row 4
  arrangeGrob(std_hwi_fdiv_analysis_geo_plot + theme_no_legend, left = "HWI"),
  arrangeGrob(
    std_hwi_fdiv_analysis_pred_plot + theme_no_legend + remove_x_scale + ylab('')
  ),
  # row 5
  arrangeGrob(std_mass_fdiv_analysis_geo_plot + theme_no_legend, left = "Mass"),
  arrangeGrob(
    std_mass_fdiv_analysis_pred_plot + theme_no_legend + remove_x_scale + ylab('')
  ),
  # row 6 - legends
  arrangeGrob(geo_fig_legend),
  arrangeGrob(pred_fig_legend),
  heights = c(0.5, 2, 2, 2, 2, 1.25),
  nrow = 6
  )

jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2.jpg'), width = 183, height = 180, units = 'mm', res = 450)
grid.arrange(
  arrangeGrob(fig2),
  ncol = 1
  )
dev.off()
null device 
          1 
pdf(filename(FIGURES_OUTPUT_DIR, 'figure2.pdf'), width = 12, height = 12, family = "Helvetica")
grid.arrange(
  arrangeGrob(fig2),
  ncol = 1
  )
dev.off()
null device 
          1 

Compare to spatial model

plot_grid(
  plot_grid(
    ggdraw() + 
    draw_label(
      "Spatial Model",
      fontface = 'bold',
      x = 0,
      hjust = 0
    ),
    ggdraw() + 
    draw_label(
      "Dredge Result",
      fontface = 'bold',
      x = 0,
      hjust = 0
    )
  ),
  plot_grid(
    std_mntd_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    std_mntd_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("MNTD", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_gape_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''),  
    std_gape_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Beak Width", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_hwi_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    std_hwi_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("HWI", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_mass_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    std_mass_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrow = 1
  ) + draw_label("Mass", size = 16, angle = 90, x = 0.01, y = 0.5), 
  nrow = 5
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
ggsave(filename(FIGURES_OUTPUT_DIR, 'process_response_vs_spatial.jpg'), width = 3000, height = 3200, units = 'px')

Compare metrics against each other

ggplot(analysis_data, aes(x = beak_width_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("Beak Width FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = beak_width_fdiv_standard, colour = core_realm)) + 
  geom_point() +
  ylab("Beak Width FDiv") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

mntd_fdiv_analysis = analysis_data %>% 
  dplyr::select(city_id,  mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard) %>%
  left_join(community_summary) %>%
  mutate(urban_pool_perc = urban_pool_size * 100 / regional_pool_size)
Joining with `by = join_by(city_id)`
mntd_fdiv_analysis
ggpairs(mntd_fdiv_analysis %>% dplyr::select(mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard, regional_pool_size, urban_pool_size, urban_pool_perc), columnLabels = c('MNTD', 'HWI FD', 'Bk FD', 'Mss FD', 'Region Rich.', 'Urban Rich.', '% Urban'))
ggsave(filename(FIGURES_OUTPUT_DIR, 'appendix_standarised_correlation.jpg'))
Saving 7.29 x 4.51 in image

LS0tCnRpdGxlOiAiTWV0cmljcyBmb3IgYXNzZXNzaW5nIGNvbW11bml0eSBhc3NlbWJseSBwcm9jZXNzZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawpiaWJsaW9ncmFwaHk6IC4uL3JlZi5iaWIgIAotLS0KCmBgYHtyfQpzb3VyY2UoJy4uL2Vudi5SJykKYGBgCgpgYGB7cn0KY29tbXVuaXR5X2RhdGEgPSByZWFkX2NzdihmaWxlbmFtZShDT01NVU5JVFlfT1VUUFVUX0RJUiwgJ2NvbW11bml0eV9hc3NlbWJseV9tZXRyaWNzX3VzaW5nX3JlbGF0aXZlX2FidW5kYW5jZS5jc3YnKSkKaGVhZChjb21tdW5pdHlfZGF0YSkKY29sbmFtZXMoY29tbXVuaXR5X2RhdGEpCmBgYAoKYGBge3J9Cm1pbihjb21tdW5pdHlfZGF0YSRtbnRkX3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkbW50ZF9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWF4KGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGh3aV9mZGl2X3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkaHdpX2ZkaXZfc3RhbmRhcmQpCm1pbihjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCm1heChjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCmBgYAoKCkpvaW4gb24gcmVhbG1zCmBgYHtyfQpjaXR5X3RvX3JlYWxtID0gcmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdyZWFsbXMuY3N2JykpCmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0gPSBsZWZ0X2pvaW4oY29tbXVuaXR5X2RhdGEsIGNpdHlfdG9fcmVhbG0pCmBgYAoKQ2l0aWVzIGFzIHBvaW50cwpgYGB7cn0KY2l0eV9wb2ludHMgPSBzdF9jZW50cm9pZChyZWFkX3NmKGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAnY2l0eV9zZWxlY3Rpb24uc2hwJykpKSAlPiUgbGVmdF9qb2luKGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCmNpdHlfcG9pbnRzX2Nvb3JkcyA9IHN0X2Nvb3JkaW5hdGVzKGNpdHlfcG9pbnRzKQpjaXR5X3BvaW50cyRsYXRpdHVkZSA9IGNpdHlfcG9pbnRzX2Nvb3Jkc1ssMV0KY2l0eV9wb2ludHMkbG9uZ2l0dWRlID0gY2l0eV9wb2ludHNfY29vcmRzWywyXQpgYGAKICAKYGBge3J9CndvcmxkX21hcCA9IHJlYWRfY291bnRyeV9ib3VuZGFyaWVzKCkKYGBgCgpMb2FkIGNvbW11bml0eSBkYXRhLCBhbmQgY3JlYXRlIGxvbmcgZm9ybWF0IHZlcnNpb24KYGBge3J9CmNvbW11bml0aWVzID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdjb21tdW5pdGllc19mb3JfYW5hbHlzaXMuY3N2JykpCmNvbW11bml0aWVzCmBgYAoKYGBge3J9CmNvbW11bml0eV9zdW1tYXJ5ID0gY29tbXVuaXRpZXMgJT4lIGdyb3VwX2J5KGNpdHlfaWQpICU+JSBzdW1tYXJpc2UocmVnaW9uYWxfcG9vbF9zaXplID0gbigpLCB1cmJhbl9wb29sX3NpemUgPSBzdW0ocmVsYXRpdmVfYWJ1bmRhbmNlX3Byb3h5ID4gMCkpCmNvbW11bml0eV9zdW1tYXJ5CmBgYAoKTG9hZCB0cmFpdCBkYXRhCmBgYHtyfQp0cmFpdHMgPSByZWFkX2NzdihmaWxlbmFtZShUQVhPTk9NWV9PVVRQVVRfRElSLCAndHJhaXRzX2ViaXJkLmNzdicpKQpoZWFkKHRyYWl0cykKYGBgCkxvYWQgc3BhdGlhbCB2YXIKYGBge3J9CnNwYXRpYWxfdmFyID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdzcGF0aWFsX3Zhci5jc3YnKSkgJT4lIGZpbHRlcihjaXR5X2lkICVpbiUgY29tbXVuaXR5X3N1bW1hcnkkY2l0eV9pZCkKc3BhdGlhbF92YXIKYGBgCgojIFN1bW1hcnkgbWV0cmljcyBieSBSZWFsbQpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMgPSBmdW5jdGlvbihuYW1lLCBkZikgewogIGNhdChwYXN0ZSgKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdNTlREJyksIGRmJG1udGRfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ0JlYWsgV2lkdGggRkRpdicpLCBkZiRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ0hXSSBGRGl2JyksIGRmJGh3aV9mZGl2X3N0YW5kYXJkKSwKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdNYXNzIEZEaXYnKSwgZGYkbWFzc19mZGl2X3N0YW5kYXJkKSwKICAgIHBhc3RlKCdOJywgbnJvdyhkZikpLAogICAgc2VwID0gIlxuIikpCn0KYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0dsb2JhbCcsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCmBgYAoKYGBge3J9CnVuaXF1ZShjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdOZWFyY3RpYycsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdOZWFyY3RpYycsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ05lb3Ryb3BpYycsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdOZW90cm9waWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdQYWxlYXJjdGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ1BhbGVhcmN0aWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdBZnJvdHJvcGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ0Fmcm90cm9waWMnLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdJbmRvbWFsYXlhbicsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtID09ICdJbmRvbWFsYXlhbicsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0F1c3RyYWxhc2lhJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ0F1c3RyYWxhc2lhJyxdKQpgYGAKCiMjIEhvdyBzaWduaWZpY2FudCBhcmUgdGhvc2UgZGlmZmVyZW5jZXM6CmBgYHtyfQprcnVza2FsLnRlc3QobW50ZF9zdGFuZGFyZH5jb3JlX3JlYWxtLCBkYXRhID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSkKcGFpcndpc2Uud2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRtbnRkX3N0YW5kYXJkLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCmBgYApgYGB7cn0Ka3J1c2thbC50ZXN0KGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZH5jb3JlX3JlYWxtLCBkYXRhID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSkKcGFpcndpc2Uud2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSkKYGBgCgpgYGB7cn0Ka3J1c2thbC50ZXN0KGh3aV9mZGl2X3N0YW5kYXJkfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGh3aV9mZGl2X3N0YW5kYXJkLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCmBgYAoKYGBge3J9CmtydXNrYWwudGVzdChtYXNzX2ZkaXZfc3RhbmRhcmR+Y29yZV9yZWFsbSwgZGF0YSA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCnBhaXJ3aXNlLndpbGNveC50ZXN0KGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kbWFzc19mZGl2X3N0YW5kYXJkLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCmBgYAoKIyBTdW1tYXJ5IG1ldHJpY3MgYnkgaW52YXNpdmUgc3BlY2llcwpgYGB7cn0KY2l0aWVzX3dpdGhfaW50cm9kdWNlZF9zcGVjaWVzID0gY29tbXVuaXRpZXMgJT4lIGZpbHRlcihvcmlnaW4gPT0gJ0ludHJvZHVjZWQnKSAlPiUgc2VsZWN0KGNpdHlfaWQpICU+JSBkaXN0aW5jdCgpCgpjaXRpZXNfd2l0aF9ub19pbnRyb2R1Y2VkX3NwZWNpZXMgPSBjb21tdW5pdGllcyAlPiUgZmlsdGVyKCEoY2l0eV9pZCAlaW4lIGNpdGllc193aXRoX2ludHJvZHVjZWRfc3BlY2llcyRjaXR5X2lkKSkgJT4lIHNlbGVjdChjaXR5X2lkKSAlPiUgZGlzdGluY3QoKQoKY2l0aWVzX3dpdGhfaW50cm9kdWNlZF9zcGVjaWVzJGludHJvZHVjZWRfc3BlY2llcyA9IFRSVUUKY2l0aWVzX3dpdGhfbm9faW50cm9kdWNlZF9zcGVjaWVzJGludHJvZHVjZWRfc3BlY2llcyA9IEZBTFNFCgpjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0gJT4lIGxlZnRfam9pbihyYmluZChjaXRpZXNfd2l0aF9pbnRyb2R1Y2VkX3NwZWNpZXMsIGNpdGllc193aXRoX25vX2ludHJvZHVjZWRfc3BlY2llcykpCmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkCmBgYApgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ1dpdGggSW50cm9kdWNlZCcsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnV2l0aG91dCBJbnRyb2R1Y2VkJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWRbIWNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCiMjIEhvdyBzaWduaWZpY2FudCBhcmUgdGhvc2UgZGlmZmVyZW5jZXM6CmBgYHtyfQp3aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRtbnRkX3N0YW5kYXJkfmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkYmVha193aWR0aF9mZGl2X3N0YW5kYXJkfmNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaHdpX2ZkaXZfc3RhbmRhcmR+Y29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRtYXNzX2ZkaXZfc3RhbmRhcmR+Y29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKIyBJbnZhc2l2ZSBzcGVjaWVzIHBlciByZWFsbQojIyBOZWFyY3RpYwpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ05lYXJjdGljIFdpdGggSW50cm9kdWNlZCcsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGNvcmVfcmVhbG0gPT0gJ05lYXJjdGljJyAmIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCiMjIE5lb3Ryb3BpYwpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ05lb3Ryb3BpYyBXaXRoIEludHJvZHVjZWQnLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZFtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRjb3JlX3JlYWxtID09ICdOZW90cm9waWMnICYgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzLF0pCmBgYAojIyBQYWxlYXJjdGljCmBgYHtyfQpwYWxlYXJjdGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWRbY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkY29yZV9yZWFsbSA9PSAnUGFsZWFyY3RpYycsXQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnUGFsZWFyY3RpYyBXaXRoIEludHJvZHVjZWQnLCBwYWxlYXJjdGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW3BhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzLF0pCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdQYWxlYXJjdGljIFdpdGhvdXQgSW50cm9kdWNlZCcsIHBhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWRbIXBhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzLF0pCmBgYAojIyMgTU5URApgYGB7cn0Kd2lsY294LnRlc3QocGFsZWFyY3RpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRtbnRkX3N0YW5kYXJkfnBhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKIyMjIEJlYWsgd2lkdGgKYGBge3J9CndpbGNveC50ZXN0KHBhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkYmVha193aWR0aF9mZGl2X3N0YW5kYXJkfnBhbGVhcmN0aWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKIyMjIEhXSQpgYGB7cn0Kd2lsY294LnRlc3QocGFsZWFyY3RpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRod2lfZmRpdl9zdGFuZGFyZH5wYWxlYXJjdGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCiMjIyBNYXNzCmBgYHtyfQp3aWxjb3gudGVzdChwYWxlYXJjdGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJG1hc3NfZmRpdl9zdGFuZGFyZH5wYWxlYXJjdGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCgojIyBBZnJvdHJvcGljCgpgYGB7cn0KYWZyb3Ryb3BpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGNvcmVfcmVhbG0gPT0gJ0Fmcm90cm9waWMnLF0KYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0Fmcm90cm9waWMgV2l0aCBJbnRyb2R1Y2VkJywgYWZyb3Ryb3BpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZFthZnJvdHJvcGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnQWZyb3Ryb3BpYyBXaXRob3V0IEludHJvZHVjZWQnLCBhZnJvdHJvcGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkWyFhZnJvdHJvcGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCiMjIyBNTlRECmBgYHtyfQp3aWxjb3gudGVzdChhZnJvdHJvcGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJG1udGRfc3RhbmRhcmR+YWZyb3Ryb3BpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRpbnRyb2R1Y2VkX3NwZWNpZXMpCmBgYAojIyMgQmVhayB3aWR0aApgYGB7cn0Kd2lsY294LnRlc3QoYWZyb3Ryb3BpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmR+YWZyb3Ryb3BpY19jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRpbnRyb2R1Y2VkX3NwZWNpZXMpCmBgYAojIyMgSFdJCmBgYHtyfQp3aWxjb3gudGVzdChhZnJvdHJvcGljX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGh3aV9mZGl2X3N0YW5kYXJkfmFmcm90cm9waWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKIyMjIE1hc3MKYGBge3J9CndpbGNveC50ZXN0KGFmcm90cm9waWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkbWFzc19mZGl2X3N0YW5kYXJkfmFmcm90cm9waWNfY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKCiMjIEluZG9tYWxheWFuCmBgYHtyfQppbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGNvcmVfcmVhbG0gPT0gJ0luZG9tYWxheWFuJyxdCmBgYAoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdJbmRvbWFsYXlhbiBXaXRoIEludHJvZHVjZWQnLCBpbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZFtpbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRpbnRyb2R1Y2VkX3NwZWNpZXMsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0luZG9tYWxheWFuIFdpdGhvdXQgSW50cm9kdWNlZCcsIGluZG9tYWxheWFuX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkWyFpbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRpbnRyb2R1Y2VkX3NwZWNpZXMsXSkKYGBgCgojIyMgTU5URApgYGB7cn0Kd2lsY294LnRlc3QoaW5kb21hbGF5YW5fY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkbW50ZF9zdGFuZGFyZH5pbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRpbnRyb2R1Y2VkX3NwZWNpZXMpCmBgYAojIyMgQmVhayB3aWR0aApgYGB7cn0Kd2lsY294LnRlc3QoaW5kb21hbGF5YW5fY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkYmVha193aWR0aF9mZGl2X3N0YW5kYXJkfmluZG9tYWxheWFuX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCiMjIyBIV0kKYGBge3J9CndpbGNveC50ZXN0KGluZG9tYWxheWFuX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGh3aV9mZGl2X3N0YW5kYXJkfmluZG9tYWxheWFuX2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcykKYGBgCiMjIyBNYXNzCmBgYHtyfQp3aWxjb3gudGVzdChpbmRvbWFsYXlhbl9jb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtX3dpdGhfaW50cm9kdWNlZCRtYXNzX2ZkaXZfc3RhbmRhcmR+aW5kb21hbGF5YW5fY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbV93aXRoX2ludHJvZHVjZWQkaW50cm9kdWNlZF9zcGVjaWVzKQpgYGAKCiMjIEF1c3RyYWxhc2lhCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0F1c3RyYWxhc2lhIFdpdGggSW50cm9kdWNlZCcsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGNvcmVfcmVhbG0gPT0gJ0F1c3RyYWxhc2lhJyAmIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1fd2l0aF9pbnRyb2R1Y2VkJGludHJvZHVjZWRfc3BlY2llcyxdKQpgYGAKCgojIFdoYXQgZmFtaWxpZXMgZXhpc3QgaW4gd2hpY2ggcmVhbG1zPwpgYGB7cn0KY29tbXVuaXRpZXMgJT4lIAogIGxlZnRfam9pbihjaXR5X3RvX3JlYWxtKSAlPiUgCiAgbXV0YXRlKGZhbWlseSA9IGdzdWIoICIgLiokIiwgIiIsIGViaXJkX3NwZWNpZXNfbmFtZSkpICU+JQogIGRwbHlyOjpzZWxlY3QoZmFtaWx5LCBjb3JlX3JlYWxtKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoY29yZV9yZWFsbSkKYGBgCgojIyBUb3RhbCB1cmJhbiBmYW1pbGllcwpgYGB7cn0KY29tbXVuaXRpZXMgJT4lIAogIG11dGF0ZShmYW1pbHkgPSBnc3ViKCAiIC4qJCIsICIiLCBlYmlyZF9zcGVjaWVzX25hbWUpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGZhbWlseSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKCkKYGBgCgpvZiB3aGljaCB1cmJhbgpgYGB7cn0KY29tbXVuaXRpZXMgJT4lIAogIGZpbHRlcihyZWxhdGl2ZV9hYnVuZGFuY2VfcHJveHkgPiAwKSAlPiUKICBtdXRhdGUoZmFtaWx5ID0gZ3N1YiggIiAuKiQiLCAiIiwgZWJpcmRfc3BlY2llc19uYW1lKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmYW1pbHkpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZSgpCmBgYAoKIyBTdW1tYXJ5IG1ldHJpY3MgYnkgaW50cm9kdWNlZCBzcGVjaWVzCmBgYHtyfQpjb21tdW5pdGllcyA9IHJlYWRfY3N2KGZpbGVuYW1lKENPTU1VTklUWV9PVVRQVVRfRElSLCAnY29tbXVuaXRpZXNfZm9yX2FuYWx5c2lzLmNzdicpKQpjaXR5X2ludHJvZHVjZWRfc3BlY2llcyA9IGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgc3VtbWFyaXNlKG51bWJlcl9vZl9zcGVjaWVzID0gbigpKSAlPiUgbGVmdF9qb2luKAogIGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgZmlsdGVyKG9yaWdpbiA9PSAnSW50cm9kdWNlZCcpICU+JSBzdW1tYXJpc2UobnVtYmVyX29mX2ludHJvZHVjZWRfc3BlY2llcyA9IG4oKSkKKSAlPiUgcmVwbGFjZV9uYShsaXN0KG51bWJlcl9vZl9pbnRyb2R1Y2VkX3NwZWNpZXMgPSAwKSkKCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyA9IGxlZnRfam9pbihjb21tdW5pdHlfZGF0YSwgY2l0eV9pbnRyb2R1Y2VkX3NwZWNpZXMpCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyRoYXNfaW50cm9kdWNlZF9zcGVjaWVzID0gY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zJG51bWJlcl9vZl9pbnRyb2R1Y2VkX3NwZWNpZXMgPiAwCmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucwpgYGAKCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgZmlsdGVyKG9yaWdpbiA9PSAnSW50cm9kdWNlZCcpICU+JQogIGRwbHlyOjpzZWxlY3QoZWJpcmRfc3BlY2llc19uYW1lKSAlPiUKICBncm91cF9ieShlYmlyZF9zcGVjaWVzX25hbWUpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9jaXRpZXMgPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyh0b3RhbF9jaXRpZXMpKQpgYGAKCmBgYHtyfQpjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnNbLGMoJ21udGRfc3RhbmRhcmQnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycpXQpgYGAKCmBgYHtyfQpjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMgJT4lIGdyb3VwX2J5KGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMpICU+JSBzdW1tYXJpc2UoCiAgdG90YWxfY2l0aWVzID0gbigpLCAKICAKICBtZWFuX21udGRfc3RkID0gbWVhbihtbnRkX3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9tbnRkX3N0ZCA9IG1lZGlhbihtbnRkX3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIHNkX21udGRfc3RkID0gc2QobW50ZF9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX21hc3NfZmRpdl9zdGQgPSBtZWFuKG1hc3NfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5fbWFzc19mZGl2X3N0ZCA9IG1lZGlhbihtYXNzX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfbWFzc19mZGl2X3N0ZCA9IHNkKG1hc3NfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX2dhcGVfd2lkdGhfZmRpdl9zdGQgPSBtZWFuKGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5fZ2FwZV93aWR0aF9mZGl2X3N0ZCA9IG1lZGlhbihiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfZ2FwZV93aWR0aF9mZGl2X3N0ZCA9IHNkKGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICAKICBtZWFuX2hhbmR3aW5nX2luZGV4X2ZkaXZfc3RkID0gbWVhbihod2lfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBtZWRpYW5faGFuZHdpbmdfaW5kZXhfZmRpdl9zdGQgPSBtZWRpYW4oaHdpX2ZkaXZfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfaGFuZHdpbmdfaW5kZXhfZmRpdl9zdGQgPSBzZChod2lfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKQopCmBgYAoKIyMgTU5URApgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gbW50ZF9zdGFuZGFyZCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QobW50ZF9zdGFuZGFyZCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTPCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjQ3wrEwLjE5KSAocC12YWx1ZSA9IDAuMDIpLgoKCiMjIE1hc3MgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gbWFzc19mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChtYXNzX2ZkaXZfc3RhbmRhcmQgfiBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIG5hLmFjdGlvbiA9ICduYS5vbWl0JykKYGBgClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTfCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjczwrEwLjI0KSAocCA8IDAuMDAwMSkKCgojIyBCZWFrIEdhcGUgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gYmVha193aWR0aF9mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQgfiBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIG5hLmFjdGlvbiA9ICduYS5vbWl0JykKYGBgClRoZXJlIGlzIE5PVCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmVzcG9uc2Ugb2YgY2l0aWVzIHdpdGggaW50cm9kdWNlZCBzcGVjaWVzICgwLjYxwrEwLjMwKSBhbmQgdGhvc2Ugd2l0aG91dCAoMC41NsKxMC4yNykKCgojIyBIV0kgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gaHdpX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKYGBge3J9CndpbGNveC50ZXN0KGh3aV9mZGl2X3N0YW5kYXJkIH4gaGFzX2ludHJvZHVjZWRfc3BlY2llcywgY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBuYS5hY3Rpb24gPSAnbmEub21pdCcpCmBgYApUaGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmVzcG9uc2Ugb2YgY2l0aWVzIHdpdGggaW50cm9kdWNlZCBzcGVjaWVzICgwLjQ5wrEwLjMwKSBhbmQgdGhvc2Ugd2l0aG91dCAoMC43OcKxMC4yMSkgKHAgPCAwLjAwMDEpCgojIyBXaGF0IHByb3BvcnRpb24gb2YgY2l0aWVzIGluIGVhY2ggcmVhbG0gaGF2ZSBpbnRyb2R1Y2VkIHNwZWNpZXMKYGBge3J9CmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyAlPiUgbGVmdF9qb2luKGNpdHlfdG9fcmVhbG0pICU+JQogIGdyb3VwX2J5KGNvcmVfcmVhbG0pICU+JQogIHN1bW1hcmlzZSgKICAgIHRvdGFsX2NpdGllcyA9IG4oKSwgCiAgICB0b3RhbF9jaXRpZXNfd2l0aF9pbnRyb2R1Y2VkID0gc3VtKGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMpLCAKICAgIHRvdGFsX2NpdGllc193aXRob3V0X2ludHJvZHVjZWQgPSBuKCkgLSBzdW0oaGFzX2ludHJvZHVjZWRfc3BlY2llcykpICU+JQogIGFycmFuZ2UoY29yZV9yZWFsbSkKYGBgCgojIyBBcmUgYW55IGludHJvZHVjZWQgc3BlY2llcyBub3QgYWxzbyBwcmVzZW50IGluIGEgY2l0eT8KYGBge3J9CmNvbW11bml0aWVzICU+JSAKICBmaWx0ZXIob3JpZ2luID09ICdJbnRyb2R1Y2VkJykgJT4lIAogIGZpbHRlcihyZWxhdGl2ZV9hYnVuZGFuY2VfcHJveHkgPCAwLjEpCmBgYAoKIyMgV2hhdCdzIHRoZSBhdmVyYWdlIHJlbGF0aXZlIGFidW5kYW5jZSBvZiBpbnRyb2R1Y2VkIHNwZWNpZXMgY29tcGFyZWQgdG8gbmF0aXZlCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgZ3JvdXBfYnkob3JpZ2luKSAlPiUgCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfcmVsYXRpdmVfYWJ1bmRhbmNlID0gbWVhbihyZWxhdGl2ZV9hYnVuZGFuY2VfcHJveHkpKQpgYGAKCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgZ3JvdXBfYnkob3JpZ2luKSAlPiUgCiAgZmlsdGVyKHJlbGF0aXZlX2FidW5kYW5jZV9wcm94eSA+IDApICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX3JlbGF0aXZlX2FidW5kYW5jZSA9IG1lYW4ocmVsYXRpdmVfYWJ1bmRhbmNlX3Byb3h5KSkKYGBgCgpgYGB7cn0KY29tbXVuaXRpZXMgJT4lIAogIGdyb3VwX2J5KG9yaWdpbikgJT4lIAogIHN1bW1hcmlzZShhdmVyYWdlX3JlbGF0aXZlX2FidW5kYW5jZSA9IG1lYW4ocmVsYXRpdmVfYWJ1bmRhbmNlX3Byb3h5KSkKYGBgCgojIENyZWF0ZSBhbmFseXNpcyBkYXRhIGZyYW1lCmBgYHtyfQpnZW9ncmFwaHkgPSByZWFkX2NzdihmaWxlbmFtZShDSVRZX0RBVEFfT1VUUFVUX0RJUiwgJ2dlb2dyYXBoeS5jc3YnKSkKbmFtZXMoZ2VvZ3JhcGh5KQpgYGAKCmBgYHtyfQphbmFseXNpc19kYXRhID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVssYygnY2l0eV9pZCcsICdtbnRkX3N0YW5kYXJkJywgJ21hc3NfZmRpdl9zdGFuZGFyZCcsICdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnLCAnaHdpX2ZkaXZfc3RhbmRhcmQnLCAnY29yZV9yZWFsbScpXSAlPiUgCiAgbGVmdF9qb2luKGNpdHlfcG9pbnRzWyxjKCdjaXR5X2lkJywgJ2xhdGl0dWRlJywgJ2xvbmdpdHVkZScpXSkgJT4lCiAgbGVmdF9qb2luKGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9uc1ssYygnY2l0eV9pZCcsICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzJyldKSAlPiUKICBsZWZ0X2pvaW4oZ2VvZ3JhcGh5KSAlPiUKICBsZWZ0X2pvaW4oc3BhdGlhbF92YXIpCgphbmFseXNpc19kYXRhJGFic19sYXRpdHVkZSA9IGFicyhhbmFseXNpc19kYXRhJGxhdGl0dWRlKQphbmFseXNpc19kYXRhJGNvcmVfcmVhbG0gPSBmYWN0b3IoYW5hbHlzaXNfZGF0YSRjb3JlX3JlYWxtLCBsZXZlbHMgPSBjKCdQYWxlYXJjdGljJywgJ05lYXJjdGljJywgJ05lb3Ryb3BpYycsICdBZnJvdHJvcGljJywgJ0luZG9tYWxheWFuJywgJ0F1c3RyYWxhc2lhJywgJ09jZWFuaWEnKSkKYW5hbHlzaXNfZGF0YSRoYXNfaW50cm9kdWNlZF9zcGVjaWVzID0gZmFjdG9yKGFuYWx5c2lzX2RhdGEkaGFzX2ludHJvZHVjZWRfc3BlY2llcywgbGV2ZWwgPSBjKCdGQUxTRScsICdUUlVFJyksIGxhYmVscyA9IGMoJ05vIGludHJvZHVjZWQgc3BlY2llcycsICdJbnRyb2R1Y2VkIHNwZWNpZXMnKSkKYGBgCgpgYGB7cn0KbW9kZWxfZGF0YSA9IGZ1bmN0aW9uKGRmLCBkZXBlbmRhbnRfdmFyKSB7CiAgZGZbLGMoZGVwZW5kYW50X3ZhciwgJ2NvcmVfcmVhbG0nLCAnYWJzX2xhdGl0dWRlJywgJ2xvbmdpdHVkZScsICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzJywgJ2NpdHlfYXZnX25kdmknLCAnY2l0eV9hdmdfZWxldmF0aW9uJywgJ2NpdHlfYXZnX3RlbXAnLCAnY2l0eV9hdmdfbWluX21vbnRobHlfdGVtcCcsICdjaXR5X2F2Z19tYXhfbW9udGhseV90ZW1wJywgJ2NpdHlfYXZnX21vbnRobHlfdGVtcCcsICdjaXR5X2F2Z19yYWluZmFsbCcsICdjaXR5X2F2Z19tYXhfbW9udGhseV9yYWluZmFsbCcsICdjaXR5X2F2Z19taW5fbW9udGhseV9yYWluZmFsbCcsICdjaXR5X2F2Z19zb2lsX21vaXN0dXJlJywgJ2NpdHlfbWF4X2VsZXYnLCAnY2l0eV9taW5fZWxldicsICdjaXR5X2VsZXZfcmFuZ2UnLCAncmVnaW9uXzIwa21fYXZnX25kdmknLCAncmVnaW9uXzIwa21fYXZnX2VsZXZhdGlvbicsICdyZWdpb25fMjBrbV9hdmdfc29pbF9tb2lzdHVyZScsICdyZWdpb25fMjBrbV9tYXhfZWxldicsICdyZWdpb25fMjBrbV9taW5fZWxldicsICdyZWdpb25fMjBrbV9lbGV2X3JhbmdlJywgJ3JlZ2lvbl81MGttX2F2Z19uZHZpJywgJ3JlZ2lvbl81MGttX2F2Z19lbGV2YXRpb24nLCAncmVnaW9uXzUwa21fYXZnX3NvaWxfbW9pc3R1cmUnLCAncmVnaW9uXzUwa21fbWF4X2VsZXYnLCAncmVnaW9uXzUwa21fbWluX2VsZXYnLCAncmVnaW9uXzUwa21fZWxldl9yYW5nZScpXQp9Cm1vZGVsX2RhdGEoYW5hbHlzaXNfZGF0YSwgJ21udGRfc3RhbmRhcmQnKQpgYGAKCmBgYHtyfQpuYW1lcyhhbmFseXNpc19kYXRhKQpgYGAKCiMjIE5NRFMgU3BhdGlhbCBIZWxwZXJzCmBgYHtyfQphbmFseXNpc19kYXRhX25tZHNfY29vcmRzID0gYW5hbHlzaXNfZGF0YVssYygnTk1EUzEnLCAnTk1EUzInKV0KY29vcmRpbmF0ZXMoYW5hbHlzaXNfZGF0YV9ubWRzX2Nvb3JkcykgID0gfiBOTURTMSArIE5NRFMyCgphbmFseXNpc19kYXRhX25tZHNfbmVhcm5laWdoIDwtIGtuZWFybmVpZ2goYW5hbHlzaXNfZGF0YV9ubWRzX2Nvb3JkcykKYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMgPC0ga25uMm5iKGFuYWx5c2lzX2RhdGFfbm1kc19uZWFybmVpZ2gpCmBgYAoKIyMjIFBvbHlnb25zIGFyb3VuZCByZWFsbXMgaW4gTk1EUyBwbG90CmBgYHtyfQpjaXRpZXNfdG9fcmVhbG1zX25tZHMgPSByZWFkX2NzdihmaWxlbmFtZShDSVRZX0RBVEFfT1VUUFVUX0RJUiwgJ3JlYWxtcy5jc3YnKSkgJT4lIGxlZnRfam9pbihhbmFseXNpc19kYXRhKSAlPiUgZmlsdGVyKCFpcy5uYShOTURTMSkpCnVuaXF1ZShjaXRpZXNfdG9fcmVhbG1zX25tZHMkY29yZV9yZWFsbSkKcmVhbG1fbm1kc19uZWFydGljX3BvbHlnb24gPSBjaXRpZXNfdG9fcmVhbG1zX25tZHMgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdOZWFyY3RpYycpICU+JSBzbGljZShjaHVsbChOTURTMSwgTk1EUzIpKQpyZWFsbV9ubWRzX25lb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKE5NRFMxLCBOTURTMikpCnJlYWxtX25tZHNfcGFsZWFyY3RpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnUGFsZWFyY3RpYycpICU+JSBzbGljZShjaHVsbChOTURTMSwgTk1EUzIpKQpyZWFsbV9ubWRzX2Fmcm90cm9waWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbm1kcyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ0Fmcm90cm9waWMnKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fbm1kc19pbmRvbWFsYXlhbl9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnSW5kb21hbGF5YW4nKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fbm1kc19hdXN0cmFsYXNpYV9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnQXVzdHJhbGFzaWEnKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKCnBvbHlnb25fbGluZV90eXBlID0gJ2Rhc2hlZCcKcG9seWdvbl9saW5ld2lkdGggPSAwLjQKCndpdGhfcmVhbG1zX25tZHMgPSBmdW5jdGlvbihnKSB7CiAgZyArIAogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX25lYXJ0aWNfcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkgKwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX25lb3Ryb3BpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfcGFsZWFyY3RpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfYWZyb3Ryb3BpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfaW5kb21hbGF5YW5fcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkgKwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX2F1c3RyYWxhc2lhX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApCn0KYGBgCgojIyBMYXQgTG9uZyBTcGF0aWFsIEhlbHBlcnMKYGBge3J9CmFuYWx5c2lzX2RhdGFfbGF0bG9uZ19jb29yZHMgPSBhbmFseXNpc19kYXRhWyxjKCdsb25naXR1ZGUnLCAnbGF0aXR1ZGUnKV0KY29vcmRpbmF0ZXMoYW5hbHlzaXNfZGF0YV9sYXRsb25nX2Nvb3JkcykgID0gfiBsb25naXR1ZGUgKyBsYXRpdHVkZQoKYW5hbHlzaXNfZGF0YV9jb29yZHNfbmVhcm5laWdoIDwtIGtuZWFybmVpZ2goYW5hbHlzaXNfZGF0YV9sYXRsb25nX2Nvb3JkcywgbG9uZ2xhdCA9IFRSVUUpCmFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycyA8LSBrbm4ybmIoYW5hbHlzaXNfZGF0YV9jb29yZHNfbmVhcm5laWdoKQpgYGAKCiMjIyBQb2x5Z29ucyBhcm91bmQgcmVhbG1zIGluIGxhdCBsb25nIHBsb3QKYGBge3J9CmNpdGllc190b19yZWFsbXNfbGF0bG9uZyA9IHJlYWRfY3N2KGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAncmVhbG1zLmNzdicpKSAlPiUgbGVmdF9qb2luKGFuYWx5c2lzX2RhdGEpICU+JSBmaWx0ZXIoIWlzLm5hKGxhdGl0dWRlKSkKdW5pcXVlKGNpdGllc190b19yZWFsbXNfbGF0bG9uZyRjb3JlX3JlYWxtKQpyZWFsbV9sYXRsb25nX25lYXJ0aWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ05lYXJjdGljJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQpyZWFsbV9sYXRsb25nX25lb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQpyZWFsbV9sYXRsb25nX3BhbGVhcmN0aWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ1BhbGVhcmN0aWMnKSAlPiUgc2xpY2UoY2h1bGwobGF0aXR1ZGUsIGxvbmdpdHVkZSkpCnJlYWxtX2xhdGxvbmdfYWZyb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnQWZyb3Ryb3BpYycpICU+JSBzbGljZShjaHVsbChsYXRpdHVkZSwgbG9uZ2l0dWRlKSkKcmVhbG1fbGF0bG9uZ19pbmRvbWFsYXlhbl9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnSW5kb21hbGF5YW4nKSAlPiUgc2xpY2UoY2h1bGwobGF0aXR1ZGUsIGxvbmdpdHVkZSkpCnJlYWxtX2xhdGxvbmdfYXVzdHJhbGFzaWFfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ0F1c3RyYWxhc2lhJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQoKd2l0aF9yZWFsbXNfbGF0bG9uZyA9IGZ1bmN0aW9uKGcpIHsKICBnICsgCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfbmVhcnRpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfbmVvdHJvcGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19wYWxlYXJjdGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19hZnJvdHJvcGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19pbmRvbWFsYXlhbl9wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfYXVzdHJhbGFzaWFfcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkKfQpgYGAKCiMgQ2hlY2sgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uCgojIyBNTlRECgojIyMgTGF0L0xvbmcKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IG1udGRfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtbnRkX3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQpgYGAKCiMjIyBOTURTCmBgYHtyfQp3aXRoX3JlYWxtc19ubWRzKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IG1udGRfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtbnRkX3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKIyMgRkRpdiBCZWFrIFdpZHRoCgojIyMgTGF0L0xvbmcKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKYGBgCgojIyMgTk1EUwpgYGB7cn0Kd2l0aF9yZWFsbXNfbm1kcyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgoKIyMgRkRpdiBIV0kKCiMjIyBMYXQvTG9uZwpgYGB7cn0Kd2l0aF9yZWFsbXNfbGF0bG9uZyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBsYXRpdHVkZSwgeSA9IGxvbmdpdHVkZSwgY29sb3VyID0gaHdpX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRod2lfZmRpdl9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKYGBgCgojIyMgTk1EUwpgYGB7cn0Kd2l0aF9yZWFsbXNfbm1kcyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBod2lfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJGh3aV9mZGl2X3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKCiMjIEZEaXYgTWFzcwoKIyMjIExhdC9Mb25nCmBgYHtyfQp3aXRoX3JlYWxtc19sYXRsb25nKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGxhdGl0dWRlLCB5ID0gbG9uZ2l0dWRlLCBjb2xvdXIgPSBtYXNzX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCmBgYAoKIyMjIE5NRFMKYGBge3J9CndpdGhfcmVhbG1zX25tZHMoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gTk1EUzEsIHkgPSBOTURTMiwgY29sb3VyID0gbWFzc19mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIlN0YW5kYXJkaXNlZCByZXNwb25zZSIpKQpgYGAKCmBgYHtyfQptb3Jhbi50ZXN0KGFuYWx5c2lzX2RhdGEkbWFzc19mZGl2X3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKCiMgRXhhbWluZSBpbmRpdmlkdWFsIG1ldHJpY3MKYGBge3J9CmFsbF9leHBsYW5hdG9yaWVzID0gYygKICAgICdjaXR5X2F2Z19uZHZpJywgJ2NpdHlfYXZnX2VsZXZhdGlvbicsICdjaXR5X2F2Z190ZW1wJywKICAgICdyZWdpb25fNTBrbV9hdmdfc29pbF9tb2lzdHVyZScsCiAgICAnY29yZV9yZWFsbUFmcm90cm9waWMnLCAnY29yZV9yZWFsbUF1c3RyYWxhc2lhJywgJ2NvcmVfcmVhbG1JbmRvbWFsYXlhbicsICdjb3JlX3JlYWxtTmVhcmN0aWMnLCAnY29yZV9yZWFsbU5lb3Ryb3BpYycsICdjb3JlX3JlYWxtUGFsZWFyY3RpYycsCiAgICAnaGFzX2ludHJvZHVjZWRfc3BlY2llc05vIGludHJvZHVjZWQgc3BlY2llcycsICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJwopCgphbGxfZXhwbGFuYXRvcnlfbmFtZXMgPSBmYWN0b3IoCiAgIGMoCiAgICAnQXZnLiBORFZJJywgJ0F2Zy4gRWxldmF0aW9uJywgJ0F2Zy4gVGVtcC4nLAogICAgJ0F2Zy4gU29pbCBNb2lzdHVyZScsCiAgICAnQWZyb3Ryb3BpYycsICdBdXN0cmFsYXNpYScsICdJbmRvbWFsYXlhbicsICdOZWFyY3RpYycsICdOZW90cm9waWMnLCAnUGFsZWFyY3RpYycsCiAgICAnSW50cm9kdWNlZCBBYnNlbnQnLCAnSW50cm9kdWNlZCBQcmVzZW50JwogICksIG9yZGVyZWQgPSBUCikKCmV4cGxhbmF0b3J5X2RpY3Rpb25hcnkgPSBkYXRhLmZyYW1lKGV4cGxhbmF0b3J5ID0gYWxsX2V4cGxhbmF0b3JpZXMsIGV4cGxhbmF0b3J5X25hbWUgPSBhbGxfZXhwbGFuYXRvcnlfbmFtZXMpCiAgCndpdGhfZXhwbGFuYXRvcnlfdHlwZV9sYWJlbHMgPSBmdW5jdGlvbihwKSB7CiAgcCA9IHBbcCRleHBsYW5hdG9yeSAhPSAnKEludGVyY2VwdCknLF0KICBleHBsYW5hdG9yeV9sZXZlbHMgPSBhbGxfZXhwbGFuYXRvcmllc1thbGxfZXhwbGFuYXRvcmllcyAlaW4lIHAkZXhwbGFuYXRvcnldCiAgcCRleHBsYW5hdG9yeSA8LSBmYWN0b3IocCRleHBsYW5hdG9yeSwgbGV2ZWxzID0gZXhwbGFuYXRvcnlfbGV2ZWxzKQogIAogIHAkdHlwZSA8LSAnUmVhbG0nCiAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdjaXR5X2F2Z19uZHZpJywgJ2NpdHlfYXZnX2VsZXZhdGlvbicsICdjaXR5X2F2Z190ZW1wJyldIDwtICdDaXR5IGdlb2dyYXBoeScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJyldIDwtICdSZWdpb25hbCAoNTAga20pIGdlb2dyYXBoeScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNObyBpbnRyb2R1Y2VkIHNwZWNpZXMnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycpXSA8LSAnSW50cm9kdWNlZCBzcGVjaWVzJwogIHAKfQoKd2l0aF9leHBsYW5hdG9yeV9uYW1lcyA9IGZ1bmN0aW9uKHApIHsKICBwICU+JSBsZWZ0X2pvaW4oZXhwbGFuYXRvcnlfZGljdGlvbmFyeSkgJT4lIGFycmFuZ2UoZGVzYyhleHBsYW5hdG9yeV9uYW1lKSkKfQoKdHlwZV9sYWJlbHMgPSBmdW5jdGlvbihwKSB7CiAgZXhwbGFuYXRvcnlfbGV2ZWxzID0gYWxsX2V4cGxhbmF0b3JpZXNbYWxsX2V4cGxhbmF0b3JpZXMgJWluJSBwJGV4cGxhbmF0b3J5XQogIHAkZXhwbGFuYXRvcnkgPC0gZmFjdG9yKHAkZXhwbGFuYXRvcnksIGxldmVscyA9IGV4cGxhbmF0b3J5X2xldmVscykKICAKICBwJHR5cGUgPC0gJ1JlYWxtJwogIHAkdHlwZVtwJGV4cGxhbmF0b3J5ICVpbiUgYygnY2l0eV9hdmdfbmR2aScsICdjaXR5X2F2Z19lbGV2YXRpb24nLCAnY2l0eV9hdmdfdGVtcCcsICdjaXR5X2F2Z19taW5fbW9udGhseV90ZW1wJywgJ2NpdHlfYXZnX21heF9tb250aGx5X3RlbXAnLCAKICAgICdjaXR5X2F2Z19tb250aGx5X3RlbXAnLCAnY2l0eV9hdmdfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWF4X21vbnRobHlfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWluX21vbnRobHlfcmFpbmZhbGwnLCAKICAgICdjaXR5X2F2Z19zb2lsX21vaXN0dXJlJywgJ2NpdHlfbWF4X2VsZXYnLCAnY2l0eV9taW5fZWxldicsICdjaXR5X2VsZXZfcmFuZ2UnKV0gPC0gJ0NpdHkgZ2VvZ3JhcGh5JwogIHAkdHlwZVtwJGV4cGxhbmF0b3J5ICVpbiUgYygncmVnaW9uXzUwa21fYXZnX25kdmknLCAncmVnaW9uXzUwa21fYXZnX2VsZXZhdGlvbicsICdyZWdpb25fNTBrbV9hdmdfc29pbF9tb2lzdHVyZScsICdyZWdpb25fNTBrbV9tYXhfZWxldicsIAogICAgJ3JlZ2lvbl81MGttX21pbl9lbGV2JywgJ3JlZ2lvbl81MGttX2VsZXZfcmFuZ2UnKV0gPC0gJ1JlZ2lvbmFsICg1MCBrbSkgZ2VvZ3JhcGh5JwogICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ3JlZ2lvbl8yMGttX2F2Z19uZHZpJywgJ3JlZ2lvbl8yMGttX2F2Z19lbGV2YXRpb24nLCAncmVnaW9uXzIwa21fYXZnX3NvaWxfbW9pc3R1cmUnLCAncmVnaW9uXzIwa21fbWF4X2VsZXYnLCAKICAgICdyZWdpb25fMjBrbV9taW5fZWxldicsICdyZWdpb25fMjBrbV9lbGV2X3JhbmdlJyldIDwtICdSZWdpb25hbCAoMjAga20pIGdlb2dyYXBoeScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNObyBpbnRyb2R1Y2VkIHNwZWNpZXMnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycpXSA8LSAnSW50cm9kdWNlZCBzcGVjaWVzJwogIHAKfQpgYGAKCmBgYHtyfQpleHBsYW5hdG9yeV9sYWJlbHMgPSBjKAogICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzJz0nSW50cm9kdWNlZCBzcGVjaWVzJywgCiAgJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNObyBpbnRyb2R1Y2VkIHNwZWNpZXMnPSdJbnRyb2R1Y2VkIGFic2VudCcsIAogICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJz0nSW50cm9kdWNlZCBwcmVzZW50JywKICAnY2l0eV9hdmdfbmR2aSc9J0F2ZXJhZ2UgTkRWSScsIAogICdjaXR5X2F2Z19lbGV2YXRpb24nPSdBdmVyYWdlIGVsZXZhdGlvbicsIAogICdjaXR5X2F2Z190ZW1wJz0nQXZlcmFnZSB0ZW1wZXJhdHVyZScsIAogICdjaXR5X2F2Z19taW5fbW9udGhseV90ZW1wJz0nQXZlcmFnZSBtaW5pbXVtIG1vbnRobHkgdGVtcGVyYXR1cmUnLCAKICAnY2l0eV9hdmdfbWF4X21vbnRobHlfdGVtcCc9J0F2ZXJhZ2UgbWF4aW11bSBtb250aGx5IHRlbXBlcmF0dXJlJywgCiAgJ2NpdHlfYXZnX21vbnRobHlfdGVtcCc9J0F2ZXJhZ2UgbW9udGhseSB0ZW1wZXJhdHVyZScsIAogICdjaXR5X2F2Z19yYWluZmFsbCc9J0F2ZXJhZ2UgcmFpbmZhbGwnLCAKICAnY2l0eV9hdmdfbWF4X21vbnRobHlfcmFpbmZhbGwnPSdBdmVyYWdlIG1heGltdW0gbW9udGhseSByYWluZmFsbCcsIAogICdjaXR5X2F2Z19taW5fbW9udGhseV9yYWluZmFsbCc9J0F2ZXJhZ2UgbWluaW11bSBtb250aGx5IHJhaW5mYWxsJywgCiAgJ2NpdHlfYXZnX3NvaWxfbW9pc3R1cmUnPSdBdmVyYWdlIHNvaWwgbW9pc3R1cmUnLCAKICAnY2l0eV9tYXhfZWxldic9J01heGltdW0gZWxldmF0aW9uJywgCiAgJ2NpdHlfbWluX2VsZXYnPSdNaW5pbXVtIGVsZXZhdGlvbicsIAogICdjaXR5X2VsZXZfcmFuZ2UnPSdFbGV2YXRpb24gcmFuZ2UnLCAKICAncmVnaW9uXzIwa21fYXZnX25kdmknPSdBdmVyYWdlIE5EVkknLCAKICAncmVnaW9uXzIwa21fYXZnX2VsZXZhdGlvbic9J0F2ZXJhZ2UgZWxldmF0aW9uJywgCiAgJ3JlZ2lvbl8yMGttX2F2Z19zb2lsX21vaXN0dXJlJz0nQXZlcmFnZSBzb2lsIG1vaXN0dXJlJywgCiAgJ3JlZ2lvbl8yMGttX21heF9lbGV2Jz0nTWF4aW11bSBlbGV2YXRpb24nLCAKICAncmVnaW9uXzIwa21fbWluX2VsZXYnPSdNaW5pbXVtIGVsZXZhdGlvbicsCiAgJ3JlZ2lvbl8yMGttX2VsZXZfcmFuZ2UnPSdFbGV2YXRpb24gcmFuZ2UnLAogICdyZWdpb25fNTBrbV9hdmdfbmR2aSc9J0F2ZXJhZ2UgTkRWSScsCiAgJ3JlZ2lvbl81MGttX2F2Z19lbGV2YXRpb24nPSdBdmVyYWdlIGVsZXZhdGlvbicsCiAgJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJz0nQXZlcmFnZSBzb2lsIG1vaXN0dXJlJywgCiAgJ3JlZ2lvbl81MGttX21heF9lbGV2Jz0nTWF4aW11bSBlbGV2YXRpb24nLAogICdyZWdpb25fNTBrbV9taW5fZWxldic9J01pbmltdW0gZWxldmF0aW9uJywgCiAgJ3JlZ2lvbl81MGttX2VsZXZfcmFuZ2UnPSdFbGV2YXRpb24gcmFuZ2UnLAogICdhYnNfbGF0aXR1ZGUnID0gJ0Fic29sdXRlIGxhdGl0dWRlJywKICAnbGF0aXR1ZGUnID0gJ0xhdGl0dWRlJywKICAnbG9uZ2l0dWRlJyA9ICdMb25naXR1ZGUnLAogICdjb3JlX3JlYWxtQWZyb3Ryb3BpYycgPSAnQWZyb3Ryb3BpY2FsJywgCiAgJ2NvcmVfcmVhbG1BdXN0cmFsYXNpYScgPSAnQXVzdGFsaWFzaWFuJywgCiAgJ2NvcmVfcmVhbG1JbmRvbWFsYXlhbicgPSAnSW5kb21hbGF5YW4nLCAKICAnY29yZV9yZWFsbU5lYXJjdGljJyA9ICdOZWFyY3RpYycsIAogICdjb3JlX3JlYWxtTmVvdHJvcGljJyA9ICdOZW90cm9waWNhbCcsCiAgJ2NvcmVfcmVhbG1QYWxlYXJjdGljJyA9ICdQYWxlYXJjdGljJywKICAnY29yZV9yZWFsbU9jZWFuaWEnID0gJ09jZWFuaWNhbCcpCmBgYAoKYGBge3J9CmNyZWF0ZV9mb3JtdWxhID0gZnVuY3Rpb24ocmVzcG9uc2VfdmFyKSB7CiAgYXMuZm9ybXVsYShwYXN0ZShyZXNwb25zZV92YXIsICd+IGNvcmVfcmVhbG0gKyBjaXR5X2F2Z19uZHZpICsgY2l0eV9hdmdfZWxldmF0aW9uICsgY2l0eV9hdmdfdGVtcCArIHJlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlICsgaGFzX2ludHJvZHVjZWRfc3BlY2llcycpKQp9CmBgYAoKIyMgSGVscGVyIHBsb3QgZnVuY3Rpb25zCmBgYHtyfQpnZW9tX21hcCA9IGZ1bmN0aW9uKG1hcF9zZiwgdGl0bGUpIHsKICBub3JtX21udGRfYW5hbHlzaXNfZ2VvID0gZ2dwbG90KCkgKyAKICAgIGdlb21fc2YoZGF0YSA9IHdvcmxkX21hcCwgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSArCiAgICBtYXBfc2YgKwogICAgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKwogICAgbGFicyhjb2xvdXIgPSAnU3RhbmRhcmRpc2VkXG5SZXNwb25zZScpICsKICAgIHRoZW1lX2J3KCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKQp9CmBgYAoKIyMgSGVscGVyIERyZWRnZSBmdW5jdGlvbnMKYGBge3J9CiMgVGFrZW4gZnJvbSBNdU1JTiBwYWNrYWdlCiMgaHR0cHM6Ly9yZHJyLmlvL2NyYW4vTXVNSW4vc3JjL1IvYXZlcmFnaW5nLlIKIyBodHRwczovL3JkcnIuaW8vY3Jhbi9NdU1Jbi9zcmMvUi9tb2RlbC5hdmcuUgoKLmNvZWZhcnIuYXZnIDwtCiAgZnVuY3Rpb24oY2ZhcnIsIHdlaWdodCwgcmV2aXNlZC52YXIsIGZ1bGwsIGFscGhhKSB7CQogICAgd2VpZ2h0IDwtIHdlaWdodCAvIHN1bSh3ZWlnaHQpCiAgICBuQ29lZiA8LSBkaW0oY2ZhcnIpWzNMXQogICAgaWYoZnVsbCkgewogICAgICBuYXMgPC0gaXMubmEoY2ZhcnJbLCAxTCwgXSkgJiBpcy5uYShjZmFyclssIDJMLCBdKQogICAgICBjZmFyclssIDFMLCBdW25hc10gPC0gY2ZhcnJbLCAyTCwgXVtuYXNdIDwtIDAKICAgICAgI2NmYXJyWywgMUw6MkwsIF1baXMubmEoY2ZhcnJbLCAxTDoyTCwgXSldIDwtIDAKICAgICAgaWYoIWFsbChpcy5uYShjZmFyclssIDNMLCBdKSkpCiAgICAgICAgY2ZhcnJbICwzTCwgXVtpcy5uYShjZmFyclsgLCAzTCwgXSldIDwtIEluZgogICAgfQogICAgCiAgICBhdmdjb2VmIDwtIGFycmF5KGRpbSA9IGMobkNvZWYsIDVMKSwKICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGRpbW5hbWVzKGNmYXJyKVtbM0xdXSwgYygiRXN0aW1hdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdGQuIEVycm9yIiwgIkFkanVzdGVkIFNFIiwgIkxvd2VyIENJIiwgIlVwcGVyIENJIikpKQogICAgZm9yKGkgaW4gc2VxX2xlbihuQ29lZikpCiAgICAgIGF2Z2NvZWZbaSwgXSA8LSBwYXIuYXZnKGNmYXJyWywgMUwsIGldLCBjZmFyclssIDJMLCBpXSwgd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiA9IGNmYXJyWywgM0wsIGldLCBhbHBoYSA9IGFscGhhLCByZXZpc2VkLnZhciA9IHJldmlzZWQudmFyKQogICAgCiAgICBhdmdjb2VmW2lzLm5hbihhdmdjb2VmKV0gPC0gTkEKICAgIHJldHVybihhdmdjb2VmKQogIH0KCi5tYWtlY29lZm1hdCA8LSBmdW5jdGlvbihjZikgewogIG5vLmFzZSA8LSBhbGwoaXMubmEoY2ZbLCAzTF0pKQogIHogPC0gYWJzKGNmWywgMUxdIC8gY2ZbLCBpZihuby5hc2UpIDJMIGVsc2UgM0xdKQogIHB2YWwgPC0gMiAqIHBub3JtKHosIGxvd2VyLnRhaWwgPSBGQUxTRSkKICBjYmluZChjZlssIGlmKG5vLmFzZSkgMUw6MkwgZWxzZSAxTDozTCwgZHJvcCA9IEZBTFNFXSwKICAgICAgICBgeiB2YWx1ZWAgPSB6LCBgUHIoPnx6fClgID0gemFwc21hbGwocHZhbCkpCn0KCiMgR2VuZXJhdGUgbW9kZWwgc2VsZWN0aW9ucyB1c2luZyBsbWVyLCBkcmVkZ2UsIGFuZCBtb2RlbC5hdmcKIyBgZm9ydW1sYWAgOiBhIHR3by1zaWRlZCBsaW5lYXIgZm9ybXVsYSBvYmplY3QgZGVzY3JpYmluZyBib3RoIHRoZSBmaXhlZC1lZmZlY3RzIGFuZCByYW5kb20tZWZmZWN0cyBwYXJ0IG9mIHRoZSBtb2RlbAojIGBkYXRhYCA6IHRoZSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcyBmcm9tIHRoZSBmb3JtdWxhCiMgYGFpY19kZWx0YWAgOiB0aGUgQUlDIGRlbHRhIHRvIHVzZSBmb3Igc2VsZWN0aW5nIG1vZGVscyBpbiBtb2RlbCBhdmVyYWdlCm1vZGVsX2F2ZXJhZ2UgPC0gZnVuY3Rpb24oZm9ybXVsYSwgZGF0YSwgYWljX2RlbHRhID0gMjApIHsKICBtb2RlbCA8LSBsbSgKICAgIGZvcm11bGEsCiAgICBkYXRhPWRhdGEKICApCiAgZHJlZGdlX3Jlc3VsdCA8LSBkcmVkZ2UobW9kZWwpCiAgc3VtbWFyeShtb2RlbC5hdmcoZHJlZGdlX3Jlc3VsdCwgc3Vic2V0ID0gZGVsdGEgPCBhaWNfZGVsdGEpKQp9CgojIENyZWF0ZSBhIHN1bW1hcnkgZGF0YSBmcmFtZSBjb250YWluaW5nIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgZnJvbSBhIG1vZGVsCiMgYG1vZGVsX3N1bWAgOiBUaGUgbW9kZWwgc3VtbWFyeSBvdXRwdXQgZnJvbSBgbW9kZWxfYXZlcmFnZWAKbW9kZWxfc3VtbWFyeSA8LSBmdW5jdGlvbihtb2RlbF9zdW0pIHsKICAuY29sdW1uX25hbWUgPC0gZnVuY3Rpb24ocG9zdGZpeCkgewogICAgcG9zdGZpeAogIH0KICAKICAjIGp1c3QgcmV0dXJuIHRoZSBlc3RpbWF0ZSBhbmQgcCB2YWx1ZQogIHdlaWdodCA8LSBtb2RlbF9zdW0kbXNUYWJsZVssIDVMXQogIAogIGNvZWZtYXQuZnVsbCA8LSBhcy5kYXRhLmZyYW1lKC5tYWtlY29lZm1hdCguY29lZmFyci5hdmcobW9kZWxfc3VtJGNvZWZBcnJheSwgd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0cihtb2RlbF9zdW0sICJyZXZpc2VkLnZhciIpLCBUUlVFLCAwLjA1KSkpCiAgCiAgY29lZm1hdC5zdWJzZXQgPC0KICAgIGFzLmRhdGEuZnJhbWUoLm1ha2Vjb2VmbWF0KC5jb2VmYXJyLmF2Zyhtb2RlbF9zdW0kY29lZkFycmF5LCB3ZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXR0cihtb2RlbF9zdW0sICJyZXZpc2VkLnZhciIpLCBGQUxTRSwgMC4wNSkpKQogIAogIAogIGNvZWZtYXQuc3Vic2V0IDwtIGNvZWZtYXQuc3Vic2V0Wy1jKDEpLCBjKDEsIDIsIDUpXQogIG5hbWVzKGNvZWZtYXQuc3Vic2V0KSA8LSBjKC5jb2x1bW5fbmFtZSgiZXN0aW1hdGUiKSwgLmNvbHVtbl9uYW1lKCJlcnJvciIpLCAuY29sdW1uX25hbWUoInAiKSkKICBjb2VmbWF0LnN1YnNldCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihjb2VmbWF0LnN1YnNldCwgImV4cGxhbmF0b3J5IikKICBjb2VmbWF0LnN1YnNldCRtb2RlbCA9ICdzdWJzZXQnCiAgCiAgY29lZm1hdC5mdWxsIDwtIGNvZWZtYXQuZnVsbFstYygxKSwgYygxLCAyLCA1KV0KICBuYW1lcyhjb2VmbWF0LmZ1bGwpIDwtIGMoLmNvbHVtbl9uYW1lKCJlc3RpbWF0ZSIpLCAuY29sdW1uX25hbWUoImVycm9yIiksIC5jb2x1bW5fbmFtZSgicCIpKQogIGNvZWZtYXQuZnVsbCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihjb2VmbWF0LmZ1bGwsICJleHBsYW5hdG9yeSIpCiAgY29lZm1hdC5mdWxsJG1vZGVsID0gJ2Z1bGwnCiAgCiAgcmJpbmQoY29lZm1hdC5mdWxsLCBjb2VmbWF0LnN1YnNldCkKfQpgYGAKCmBgYHtyfQpwbG90X2RyZWRnZV9yZXN1bHQgPSBmdW5jdGlvbihyZXN1bHRfdGFibGUpIHsKICBwID0gcmVzdWx0X3RhYmxlW3Jlc3VsdF90YWJsZSRtb2RlbCA9PSAnZnVsbCcsXQogIHAgPSB0eXBlX2xhYmVscyhwKQoKICBnZ3Bsb3QocCwgYWVzKHkgPSBleHBsYW5hdG9yeSwgeCA9IGVzdGltYXRlLCBjb2xvdXIgPSB0eXBlKSkgKyAKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh4bWluPWVzdGltYXRlLWVycm9yLCB4bWF4PWVzdGltYXRlK2Vycm9yKSwgd2lkdGg9LjIsCiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjA1KSkgKwogICAgc2NhbGVfeV9kaXNjcmV0ZSgKICAgICAgbGltaXRzID0gcmV2KGxldmVscyhwJGV4cGxhbmF0b3J5KSksIAogICAgICBsYWJlbHMgPSBleHBsYW5hdG9yeV9sYWJlbHMpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwoCiAgICAgIHZhbHVlcyA9IGMocmVhbG1fY29sb3VyLCBjaXR5X2dlb2dyYXBoeV9jb2xvdXIsIHJlZ2lvbmFsXzUwa21fZ2VvZ3JhcGh5X2NvbG91ciwgcmVnaW9uYWxfMjBrbV9nZW9ncmFwaHlfY29sb3VyLCBpbnRyb2R1Y2VkX3NwZWNpZXNfY29sb3VyKSwgCiAgICAgIGJyZWFrcyA9IGMoJ1JlYWxtJywgJ0NpdHkgZ2VvZ3JhcGh5JywgJ1JlZ2lvbmFsICg1MCBrbSkgZ2VvZ3JhcGh5JywgJ1JlZ2lvbmFsICgyMCBrbSkgZ2VvZ3JhcGh5JywgJ0ludHJvZHVjZWQgc3BlY2llcycpKSArCiAgICB0aGVtZV9idygpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZG90dGVkIikgKwogICAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IlByZWRpY3RvciB0eXBlIikpICsgeGxhYignRGlmZmVyZW5jZSBpbiByZXNwb25zZSBmcm9tIDBcbmhhYml0YXQgZmlsdGVyaW5nICg8IDApIGFuZCBjb21wZXRpdGl2ZSBpbnRlcmFjdGlvbnMgKD4gMClcbsKxIFN0YW5kYXJkIEVycm9yJykgKyB5bGFiKCdQcmVkaWN0b3InKSArCiAgICB0aGVtZShsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJ0b3AiKQp9CmBgYAoKIyMgR0xTIFNwYXRpYWwgSGVscGVycwpgYGB7cn0KZ2xzX21ldGhvZCA9ICJNTCIKCnNwYXRpYWxfbW9kZWwgPSBmdW5jdGlvbihmb3JtdWxhLCBjb3JyZWxhdGlvbikgewogIGdscygKICAgIGZvcm11bGEsIAogICAgZGF0YSA9IGFuYWx5c2lzX2RhdGEsIAogICAgY29ycmVsYXRpb24gPSBjb3JyZWxhdGlvbiwgCiAgICBtZXRob2QgPSBnbHNfbWV0aG9kCiAgKQp9CgpwbG90X3NwYXRpYWxfcmVzdWx0ID0gZnVuY3Rpb24obW9kZWxfcmVzdWx0KSB7CiAgbW9kZWxfc3VtbWFyeSA9IHN1bW1hcnkobW9kZWxfcmVzdWx0KQogIHJlc3VsdF90YWJsZSA9IGFzLmRhdGEuZnJhbWUobW9kZWxfc3VtbWFyeSR0VGFibGUpCiAgcmVzdWx0X3RhYmxlJGV4cGxhbmF0b3J5ID0gcm93bmFtZXMocmVzdWx0X3RhYmxlKQogIAogIHJlc3VsdF90YWJsZSA9IHJlc3VsdF90YWJsZSAlPiUgd2l0aF9leHBsYW5hdG9yeV90eXBlX2xhYmVscygpICU+JSB3aXRoX2V4cGxhbmF0b3J5X25hbWVzKCkKICAKICBnZ3Bsb3QyOjpnZ3Bsb3QocmVzdWx0X3RhYmxlLCBnZ3Bsb3QyOjphZXMoeT1mYWN0b3IoZXhwbGFuYXRvcnlfbmFtZSwgbGV2ZWwgPSBhbGxfZXhwbGFuYXRvcnlfbmFtZXMsIG9yZGVyZWQgPSBUKSwgeD1WYWx1ZSwgY29sb3VyID0gdHlwZSkpICsgCiAgICBnZ3Bsb3QyOjpnZW9tX2xpbmUoKSArCiAgICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogICAgZ2dwbG90Mjo6Z2VvbV9lcnJvcmJhcihnZ3Bsb3QyOjphZXMoeG1pbj1WYWx1ZS1TdGQuRXJyb3IsIHhtYXg9VmFsdWUrU3RkLkVycm9yKSwgd2lkdGg9LjIsCiAgICAgICAgICAgICAgICAgICBwb3NpdGlvbj1nZ3Bsb3QyOjpwb3NpdGlvbl9kb2RnZSgwLjA1KSkgKwogICAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgICBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwgbGluZXR5cGU9ImRvdHRlZCIpICsKICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInRvcCIpICsKICAgIHlsYWIoJ1ByZWRpY3RvcicpICsKICAgIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJQcmVkaWN0b3IgdHlwZSIpKSArIHhsYWIoJ0RpZmZlcmVuY2UgaW4gcmVzcG9uc2UgZnJvbSAwXG5oYWJpdGF0IGZpbHRlcmluZyAoPCAwKSBhbmQgY29tcGV0aXRpdmUgaW50ZXJhY3Rpb25zICg+IDApXG7CsSBTdGFuZGFyZCBFcnJvcicpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwoCiAgICAgIHZhbHVlcyA9IGMocmVhbG1fY29sb3VyLCBjaXR5X2dlb2dyYXBoeV9jb2xvdXIsIHJlZ2lvbmFsXzUwa21fZ2VvZ3JhcGh5X2NvbG91ciwgaW50cm9kdWNlZF9zcGVjaWVzX2NvbG91ciksIAogICAgICBicmVha3MgPSBjKCdSZWFsbScsICdDaXR5IGdlb2dyYXBoeScsICdSZWdpb25hbCAoNTAga20pIGdlb2dyYXBoeScsICdJbnRyb2R1Y2VkIHNwZWNpZXMnKSkgKwogICAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHMgPSByZXYoYWxsX2V4cGxhbmF0b3J5X25hbWVzW2FsbF9leHBsYW5hdG9yeV9uYW1lcyAlaW4lIHJlc3VsdF90YWJsZSRleHBsYW5hdG9yeV9uYW1lXSkpCn0KYGBgCgojIyMgQ2hvb3NlIGJlc3Qgc3BhdGlhbCBjb3JyZWxhdGlvbiBmdW5jdGlvbgpgYGB7cn0KQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yRXhwKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpLCBjb3JSYXRpbyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yU3BoZXIoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCmBgYApNTlREOiBjb3JSYXRpbyB3aXRoIE5NRFMgKyBsYXQvbG9uZwoKYGBge3J9CkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJyksIGNvckxpbihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJyksIGNvckV4cChmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JHYXVzKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJyksIGNvckdhdXMoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JSYXRpbyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JSYXRpbyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCmBgYApCZWFrIHdpZHRoOiBjb3JFeHAgd2l0aCBOTURTICsgbGF0L2xvbmcKCmBgYHtyfQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvckdhdXMoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JHYXVzKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JSYXRpbyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvclJhdGlvKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCmBgYAoKSFdJOiBjb3JFeHAgd2l0aCBOTURTICsgbGF0L2xvbmcKCmBgYHtyfQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbWFzc19mZGl2X3N0YW5kYXJkJyksIGNvckxpbihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yRXhwKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JHYXVzKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpgYGAKCk1hc3M6IGNvckV4cCB3aXRoIE5NRFMgKyBsYXQvbG9uZwoKYGBge3J9CmNvcnJlbGF0aW9uX2Zvcm11bGEgPSBhcy5mb3JtdWxhKCd+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZScpCgpjb3JyZWxhdGlvbl9mdW5jdGlvbl9mZGl2ID0gZnVuY3Rpb24oKSB7CiAgY29yRXhwKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKQp9Cgpjb3JyZWxhdGlvbl9mdW5jdGlvbl9tbnRkID0gZnVuY3Rpb24oKSB7CiAgY29yUmF0aW8oZm9ybSA9IGNvcnJlbGF0aW9uX2Zvcm11bGEpCn0KYGBgCgojIyBNTlRECmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IG1udGRfc3RhbmRhcmQsIGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSwgJ01OVEQnKQpzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdApgYGAKCgpgYGB7cn0Kc3RkX21udGRfYW5hbHlzaXNfZGF0YSA9IG1vZGVsX2RhdGEoYW5hbHlzaXNfZGF0YVshaXMubmEoYW5hbHlzaXNfZGF0YSRtbnRkX3N0YW5kYXJkKSxdLCAnbW50ZF9zdGFuZGFyZCcpCnN0ZF9tbnRkX2FuYWx5c2lzX2Zvcm11bGEgPSBjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpCnN0ZF9tbnRkX2FuYWx5c2lzX3Jlc3VsdCA8LSBtb2RlbF9hdmVyYWdlKHN0ZF9tbnRkX2FuYWx5c2lzX2Zvcm11bGEsIHN0ZF9tbnRkX2FuYWx5c2lzX2RhdGEpCnN0ZF9tbnRkX2FuYWx5c2lzX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkoc3RkX21udGRfYW5hbHlzaXNfcmVzdWx0KQpzdGRfbW50ZF9hbmFseXNpc19yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0Kc3RkX21udGRfYW5hbHlzaXNfcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfcmVzdWx0KHN0ZF9tbnRkX2FuYWx5c2lzX3Jlc3VsdF90YWJsZSkKc3RkX21udGRfYW5hbHlzaXNfcHJlZF9wbG90CmBgYApEbyB0aGUgcmVzaWR1YWxzIHN0aWxsIGNvbnRhaW4gc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gZnJvbSBhIGZpdHRlZCBsbT8KYGBge3J9CnN0ZF9tbnRkX2xtID0gbG0oc3RkX21udGRfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX21udGRfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfbW50ZF9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3Qoc3RkX21udGRfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9Cm1udGRfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwoc3RkX21udGRfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fbW50ZCgpKQptb3Jhbi50ZXN0KG1udGRfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3QobW50ZF9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCA9IHBsb3Rfc3BhdGlhbF9yZXN1bHQobW50ZF9zcGF0aWFsX21vZGVsKQpzdGRfbW50ZF9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdApgYGAKCiMjIEdhcGUgd2lkdGggLSBGRGl2CmBgYHtyfQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2dlb19wbG90ID0gZ2VvbV9tYXAoZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gYmVha193aWR0aF9mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdCZWFrIFdpZHRoIEZEaXYnKQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKCgpgYGB7cn0Kc3RkX2dhcGVfZmRpdl9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCksXSwgJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpCnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdCA8LSBtb2RlbF9hdmVyYWdlKHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19kYXRhKQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkoc3RkX2dhcGVfZmRpdl9hbmFseXNpc19yZXN1bHQpCnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlCmBgYAoKYGBge3J9CnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfcmVzdWx0KHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlKQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdApgYGAKCkRvIHRoZSByZXNpZHVhbHMgc3RpbGwgY29udGFpbiBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBmcm9tIGEgZml0dGVkIGxtPwpgYGB7cn0Kc3RkX2dhcGVfZmRpdl9sbSA9IGxtKHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19kYXRhKQptb3Jhbi50ZXN0KHN0ZF9nYXBlX2ZkaXZfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9nYXBlX2ZkaXZfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9CnN0ZF9nYXBlX2ZkaXZfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwoc3RkX2dhcGVfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBjb3JyZWxhdGlvbl9mdW5jdGlvbl9mZGl2KCkpCm1vcmFuLnRlc3Qoc3RkX2dhcGVfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChzdGRfZ2FwZV9mZGl2X3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9CnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgPSBwbG90X3NwYXRpYWxfcmVzdWx0KHN0ZF9nYXBlX2ZkaXZfc3BhdGlhbF9tb2RlbCkKc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdApgYGAKCiMjIEhXSSAtIEZEaXYKYGBge3J9CnN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IGh3aV9mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdIV0kgRkRpdicpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdApgYGAKCgoKYGBge3J9CnN0ZF9od2lfZmRpdl9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJGh3aV9mZGl2X3N0YW5kYXJkKSxdLCAnaHdpX2ZkaXZfc3RhbmRhcmQnKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHQgPC0gbW9kZWxfYXZlcmFnZShzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2RhdGEpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KHN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHQpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0Kc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX3Jlc3VsdChzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKYGBge3J9CnN0ZF9od2lfZmRpdl9sbSA9IGxtKHN0ZF9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfaHdpX2ZkaXZfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9od2lfZmRpdl9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kc3RkX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKHN0ZF9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBjb3JyZWxhdGlvbl9mdW5jdGlvbl9mZGl2KCkpCm1vcmFuLnRlc3Qoc3RkX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9od2lfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgPSBwbG90X3NwYXRpYWxfcmVzdWx0KHN0ZF9od2lfZmRpdl9zcGF0aWFsX21vZGVsKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIyBNYXNzIC0gRkRpdgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IG1hc3NfZmRpdl9zdGFuZGFyZCwgZ2VvbWV0cnkgPSBnZW9tZXRyeSkpLCAnTWFzcyBGRGl2JykKc3RkX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdApgYGAKCgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJG1hc3NfZmRpdl9zdGFuZGFyZCksXSwgJ21hc3NfZmRpdl9zdGFuZGFyZCcpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSA9IGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3Jlc3VsdCA8LSBtb2RlbF9hdmVyYWdlKHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX21hc3NfZmRpdl9hbmFseXNpc19kYXRhKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkoc3RkX21hc3NfZmRpdl9hbmFseXNpc19yZXN1bHQpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlCmBgYAoKYGBge3J9CnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfcmVzdWx0KHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdApgYGAKCmBgYHtyfQpzdGRfbWFzc19mZGl2X2xtID0gbG0oc3RkX21hc3NfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2RhdGEpCm1vcmFuLnRlc3Qoc3RkX21hc3NfZmRpdl9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3Qoc3RkX21hc3NfZmRpdl9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9zcGF0aWFsX21vZGVsID0gc3BhdGlhbF9tb2RlbChzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIGNvcnJlbGF0aW9uX2Z1bmN0aW9uX2ZkaXYoKSkKbW9yYW4udGVzdChzdGRfbWFzc19mZGl2X3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9tYXNzX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCA9IHBsb3Rfc3BhdGlhbF9yZXN1bHQoc3RkX21hc3NfZmRpdl9zcGF0aWFsX21vZGVsKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90CmBgYAoKIyBDcmVhdGUgcGxvdCBvZiBkaWZmZXJlbmNlcyBpbiBwcm9jZXNzIHJlc3BvbnNlCmBgYHtyfQpwcmVkX2xlZ2VuZCA8LSBnZ3B1YnI6OmdldF9sZWdlbmQoCiAgIyBjcmVhdGUgc29tZSBzcGFjZSB0byB0aGUgbGVmdCBvZiB0aGUgbGVnZW5kCiAgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDApKSArIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKG5jb2w9MikpICsgbGFicyhjb2xvciA9ICJQcmVkaWN0b3IgdHlwZSIpCikKZ2VvX2xlZ2VuZCA8LSBnZ3B1YnI6OmdldF9sZWdlbmQoCiAgIyBjcmVhdGUgc29tZSBzcGFjZSB0byB0aGUgbGVmdCBvZiB0aGUgbGVnZW5kCiAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKC04MCwgMCwgMCwgMTIpLCBsZWdlbmQudGl0bGUucG9zaXRpb24gPSAidG9wIiwgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMTAsICdtbScpKSArIGxhYnMoY29sb3IgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikKKQoKbGVnZW5kID0gcGxvdF9ncmlkKAogIGdlb19sZWdlbmQsCiAgcHJlZF9sZWdlbmQsIAogIG5yb3cgPSAxCikKbGVnZW5kCmBgYAoKYGBge3J9CnBsb3RfZ3JpZCgKICBwbG90X2dyaWQoCiAgICBzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLCAKICAgIHN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiTU5URCIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwgCiAgICBzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiQmVhayBXaWR0aCIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLCAKICAgIHN0ZF9od2lfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIkhXSSIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwgCiAgICBzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiTWFzcyIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLCAKICBsZWdlbmQsCiAgbnJvdyA9IDUKKQpnZ3NhdmUoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAncHJvY2Vzc19yZXNwb25zZS5qcGcnKSwgd2lkdGggPSAzMDAwLCBoZWlnaHQgPSAzMjAwLCB1bml0cyA9ICdweCcpCmBgYAoKYGBge3J9CnByZWRfZmlnX2xlZ2VuZCA8LSBnZ3B1YnI6OmdldF9sZWdlbmQoCiAgIyBjcmVhdGUgc29tZSBzcGFjZSB0byB0aGUgbGVmdCBvZiB0aGUgbGVnZW5kCiAgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIC0yMCkpICsgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKyBsYWJzKGNvbG9yID0gIlByZWRpY3RvciB0eXBlIikKKQpnZW9fZmlnX2xlZ2VuZCA8LSBnZ3B1YnI6OmdldF9sZWdlbmQoCiAgIyBjcmVhdGUgc29tZSBzcGFjZSB0byB0aGUgbGVmdCBvZiB0aGUgbGVnZW5kCiAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdCArIHRoZW1lKGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDApLCBsZWdlbmQudGl0bGUucG9zaXRpb24gPSAidG9wIiwgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMTAsICdtbScpKSArIGxhYnMoY29sb3IgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikKKQoKcmVtb3ZlX3hfc2NhbGUgPSAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpCnRoZW1lX25vX2xlZ2VuZCA9IHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpKQoKZmlnMiA9IGdyaWQuYXJyYW5nZSgKICAjIHJvdyAxIC0gdGl0bGVzCiAgYXJyYW5nZUdyb2IoZ3JpZDo6dGV4dEdyb2IoJ0EpIFN0YW5kYXJkaXNlZCByZXNwb25zZSBieSBjaXR5JywgeCA9IDAuMSwgaGp1c3QgPSAwLCBncD1ncGFyKGZvbnRmYWNlPSJib2xkIikpKSwKICBhcnJhbmdlR3JvYihncmlkOjp0ZXh0R3JvYignQikgU3RhbmRhcmRpc2VkIHJlc3BvbnNlIHByZWRpY3RvcnMnLCB4ID0gMC4xLCBoanVzdCA9IDAsIGdwPWdwYXIoZm9udGZhY2U9ImJvbGQiKSkpLAogICMgcm93IDIKICBhcnJhbmdlR3JvYigKICAgIHN0ZF9tbnRkX2FuYWx5c2lzX2dlb19wbG90ICsgdGhlbWVfbm9fbGVnZW5kLCAKICAgIGxlZnQgPSAiTU5URCIKICApLAogIGFycmFuZ2VHcm9iKAogICAgc3RkX21udGRfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWVfbm9fbGVnZW5kICsgcmVtb3ZlX3hfc2NhbGUgKyB5bGFiKCcnKQogICksCiAgIyByb3cgMwogIGFycmFuZ2VHcm9iKHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgKyB0aGVtZV9ub19sZWdlbmQsIGxlZnQgPSAiQmVhayBXaWR0aCIpLAogIGFycmFuZ2VHcm9iKAogICAgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZV9ub19sZWdlbmQgKyByZW1vdmVfeF9zY2FsZSArIHlsYWIoJycpCiAgKSwKICAjIHJvdyA0CiAgYXJyYW5nZUdyb2Ioc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2dlb19wbG90ICsgdGhlbWVfbm9fbGVnZW5kLCBsZWZ0ID0gIkhXSSIpLAogIGFycmFuZ2VHcm9iKAogICAgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lX25vX2xlZ2VuZCArIHJlbW92ZV94X3NjYWxlICsgeWxhYignJykKICApLAogICMgcm93IDUKICBhcnJhbmdlR3JvYihzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2dlb19wbG90ICsgdGhlbWVfbm9fbGVnZW5kLCBsZWZ0ID0gIk1hc3MiKSwKICBhcnJhbmdlR3JvYigKICAgIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWVfbm9fbGVnZW5kICsgcmVtb3ZlX3hfc2NhbGUgKyB5bGFiKCcnKQogICksCiAgIyByb3cgNiAtIGxlZ2VuZHMKICBhcnJhbmdlR3JvYihnZW9fZmlnX2xlZ2VuZCksCiAgYXJyYW5nZUdyb2IocHJlZF9maWdfbGVnZW5kKSwKICBoZWlnaHRzID0gYygwLjUsIDIsIDIsIDIsIDIsIDEuMjUpLAogIG5yb3cgPSA2CiAgKQpgYGAKCmBgYHtyfQpqcGVnKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ2ZpZ3VyZTIuanBnJyksIHdpZHRoID0gMTgzLCBoZWlnaHQgPSAxODAsIHVuaXRzID0gJ21tJywgcmVzID0gNDUwKQpncmlkLmFycmFuZ2UoCiAgYXJyYW5nZUdyb2IoZmlnMiksCiAgbmNvbCA9IDEKICApCmRldi5vZmYoKQpgYGAKCmBgYHtyfQpwZGYoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlMi5wZGYnKSwgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIsIGZhbWlseSA9ICJIZWx2ZXRpY2EiKQpncmlkLmFycmFuZ2UoCiAgYXJyYW5nZUdyb2IoZmlnMiksCiAgbmNvbCA9IDEKICApCmRldi5vZmYoKQpgYGAKCiMgQ29tcGFyZSB0byBzcGF0aWFsIG1vZGVsCgpgYGB7cn0KcGxvdF9ncmlkKAogIHBsb3RfZ3JpZCgKICAgIGdnZHJhdygpICsgCiAgICBkcmF3X2xhYmVsKAogICAgICAiU3BhdGlhbCBNb2RlbCIsCiAgICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgICB4ID0gMCwKICAgICAgaGp1c3QgPSAwCiAgICApLAogICAgZ2dkcmF3KCkgKyAKICAgIGRyYXdfbGFiZWwoCiAgICAgICJEcmVkZ2UgUmVzdWx0IiwKICAgICAgZm9udGZhY2UgPSAnYm9sZCcsCiAgICAgIHggPSAwLAogICAgICBoanVzdCA9IDAKICAgICkKICApLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0zLCAzKSkgKyB5bGFiKCcnKSwgCiAgICBzdGRfbW50ZF9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIk1OVEQiLCBzaXplID0gMTYsIGFuZ2xlID0gOTAsIHggPSAwLjAxLCB5ID0gMC41KSwKICBwbG90X2dyaWQoCiAgICBzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0zLCAzKSkgKyB5bGFiKCcnKSwgIAogICAgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIkJlYWsgV2lkdGgiLCBzaXplID0gMTYsIGFuZ2xlID0gOTAsIHggPSAwLjAxLCB5ID0gMC41KSwKICBwbG90X2dyaWQoCiAgICBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIHN0ZF9od2lfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIG5yb3cgPSAxCiAgKSArIGRyYXdfbGFiZWwoIkhXSSIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAKICAgIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0zLCAzKSkgKyB5bGFiKCcnKSwgCiAgICBucm93ID0gMQogICkgKyBkcmF3X2xhYmVsKCJNYXNzIiwgc2l6ZSA9IDE2LCBhbmdsZSA9IDkwLCB4ID0gMC4wMSwgeSA9IDAuNSksIAogIG5yb3cgPSA1CikKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ3Byb2Nlc3NfcmVzcG9uc2VfdnNfc3BhdGlhbC5qcGcnKSwgd2lkdGggPSAzMDAwLCBoZWlnaHQgPSAzMjAwLCB1bml0cyA9ICdweCcpCmBgYAoKCgojIENvbXBhcmUgbWV0cmljcyBhZ2FpbnN0IGVhY2ggb3RoZXIKYGBge3J9CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgeSA9IG1udGRfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiTU5URCIpICsgCiAgeGxhYigiQmVhayBXaWR0aCBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBtbnRkX3N0YW5kYXJkLCBjb2xvdXIgPSBjb3JlX3JlYWxtKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIk1OVEQiKSArIAogIHhsYWIoIkhXSSBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiQmVhayBXaWR0aCBGRGl2IikgKyAKICB4bGFiKCJIV0kgRkRpdiIpICsKICB0aGVtZV9idygpICsgbGFicyhjb2xvciA9ICJSZWFsbSIpCmBgYAoKYGBge3J9Cm1udGRfZmRpdl9hbmFseXNpcyA9IGFuYWx5c2lzX2RhdGEgJT4lIAogIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgIG1udGRfc3RhbmRhcmQsIGh3aV9mZGl2X3N0YW5kYXJkLCBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG1hc3NfZmRpdl9zdGFuZGFyZCkgJT4lCiAgbGVmdF9qb2luKGNvbW11bml0eV9zdW1tYXJ5KSAlPiUKICBtdXRhdGUodXJiYW5fcG9vbF9wZXJjID0gdXJiYW5fcG9vbF9zaXplICogMTAwIC8gcmVnaW9uYWxfcG9vbF9zaXplKQptbnRkX2ZkaXZfYW5hbHlzaXMKYGBgCgpgYGB7cn0KZ2dwYWlycyhtbnRkX2ZkaXZfYW5hbHlzaXMgJT4lIGRwbHlyOjpzZWxlY3QobW50ZF9zdGFuZGFyZCwgaHdpX2ZkaXZfc3RhbmRhcmQsIGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbWFzc19mZGl2X3N0YW5kYXJkLCByZWdpb25hbF9wb29sX3NpemUsIHVyYmFuX3Bvb2xfc2l6ZSwgdXJiYW5fcG9vbF9wZXJjKSwgY29sdW1uTGFiZWxzID0gYygnTU5URCcsICdIV0kgRkQnLCAnQmsgRkQnLCAnTXNzIEZEJywgJ1JlZ2lvbiBSaWNoLicsICdVcmJhbiBSaWNoLicsICclIFVyYmFuJykpCmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdhcHBlbmRpeF9zdGFuZGFyaXNlZF9jb3JyZWxhdGlvbi5qcGcnKSkKYGBgCgoK